1 package org.bouncycastle.cms;
2 
3 import java.io.IOException;
4 import java.io.InputStream;
5 
6 import org.bouncycastle.asn1.ASN1Encodable;
7 import org.bouncycastle.asn1.ASN1EncodableVector;
8 import org.bouncycastle.asn1.ASN1OctetString;
9 import org.bouncycastle.asn1.ASN1Set;
10 import org.bouncycastle.asn1.cms.Attribute;
11 import org.bouncycastle.asn1.cms.AttributeTable;
12 import org.bouncycastle.asn1.cms.AuthenticatedData;
13 import org.bouncycastle.asn1.cms.CMSAlgorithmProtection;
14 import org.bouncycastle.asn1.cms.CMSAttributes;
15 import org.bouncycastle.asn1.cms.ContentInfo;
16 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
17 import org.bouncycastle.operator.DigestCalculatorProvider;
18 import org.bouncycastle.operator.OperatorCreationException;
19 import org.bouncycastle.util.Arrays;
20 import org.bouncycastle.util.Encodable;
21 
22 /**
23  * containing class for an CMS Authenticated Data object
24  */
25 public class CMSAuthenticatedData
26     implements Encodable
27 {
28     RecipientInformationStore   recipientInfoStore;
29     ContentInfo                 contentInfo;
30 
31     private AlgorithmIdentifier macAlg;
32     private ASN1Set authAttrs;
33     private ASN1Set unauthAttrs;
34     private byte[] mac;
35     private OriginatorInformation originatorInfo;
36 
CMSAuthenticatedData( byte[] authData)37     public CMSAuthenticatedData(
38         byte[]    authData)
39         throws CMSException
40     {
41         this(CMSUtils.readContentInfo(authData));
42     }
43 
CMSAuthenticatedData( byte[] authData, DigestCalculatorProvider digestCalculatorProvider)44     public CMSAuthenticatedData(
45         byte[]    authData,
46         DigestCalculatorProvider digestCalculatorProvider)
47         throws CMSException
48     {
49         this(CMSUtils.readContentInfo(authData), digestCalculatorProvider);
50     }
51 
CMSAuthenticatedData( InputStream authData)52     public CMSAuthenticatedData(
53         InputStream    authData)
54         throws CMSException
55     {
56         this(CMSUtils.readContentInfo(authData));
57     }
58 
CMSAuthenticatedData( InputStream authData, DigestCalculatorProvider digestCalculatorProvider)59     public CMSAuthenticatedData(
60         InputStream    authData,
61         DigestCalculatorProvider digestCalculatorProvider)
62         throws CMSException
63     {
64         this(CMSUtils.readContentInfo(authData), digestCalculatorProvider);
65     }
66 
CMSAuthenticatedData( ContentInfo contentInfo)67     public CMSAuthenticatedData(
68         ContentInfo contentInfo)
69         throws CMSException
70     {
71         this(contentInfo, null);
72     }
73 
CMSAuthenticatedData( ContentInfo contentInfo, DigestCalculatorProvider digestCalculatorProvider)74     public CMSAuthenticatedData(
75         ContentInfo contentInfo,
76         DigestCalculatorProvider digestCalculatorProvider)
77         throws CMSException
78     {
79         this.contentInfo = contentInfo;
80 
81         AuthenticatedData authData = AuthenticatedData.getInstance(contentInfo.getContent());
82 
83         if (authData.getOriginatorInfo() != null)
84         {
85             this.originatorInfo = new OriginatorInformation(authData.getOriginatorInfo());
86         }
87 
88         //
89         // read the recipients
90         //
91         ASN1Set recipientInfos = authData.getRecipientInfos();
92 
93         this.macAlg = authData.getMacAlgorithm();
94 
95         this.authAttrs = authData.getAuthAttrs();
96         this.mac = authData.getMac().getOctets();
97         this.unauthAttrs = authData.getUnauthAttrs();
98 
99         //
100         // read the authenticated content info
101         //
102         ContentInfo encInfo = authData.getEncapsulatedContentInfo();
103         CMSReadable readable = new CMSProcessableByteArray(
104             encInfo.getContentType(),
105             ASN1OctetString.getInstance(encInfo.getContent()).getOctets());
106 
107         //
108         // build the RecipientInformationStore
109         //
110         if (authAttrs != null)
111         {
112             if (digestCalculatorProvider == null)
113             {
114                 throw new CMSException("a digest calculator provider is required if authenticated attributes are present");
115             }
116 
117             AttributeTable table = new AttributeTable(authAttrs);
118 
119             ASN1EncodableVector protectionAttributes = table.getAll(CMSAttributes.cmsAlgorithmProtect);
120             if (protectionAttributes.size() > 1)
121             {
122                 throw new CMSException("Only one instance of a cmsAlgorithmProtect attribute can be present");
123             }
124 
125             if (protectionAttributes.size() > 0)
126             {
127                 Attribute attr = Attribute.getInstance(protectionAttributes.get(0));
128                 if (attr.getAttrValues().size() != 1)
129                 {
130                     throw new CMSException("A cmsAlgorithmProtect attribute MUST contain exactly one value");
131                 }
132 
133                 CMSAlgorithmProtection algorithmProtection = CMSAlgorithmProtection.getInstance(attr.getAttributeValues()[0]);
134 
135                 if (!CMSUtils.isEquivalent(algorithmProtection.getDigestAlgorithm(), authData.getDigestAlgorithm()))
136                 {
137                     throw new CMSException("CMS Algorithm Identifier Protection check failed for digestAlgorithm");
138                 }
139 
140                 if (!CMSUtils.isEquivalent(algorithmProtection.getMacAlgorithm(), macAlg))
141                 {
142                     throw new CMSException("CMS Algorithm Identifier Protection check failed for macAlgorithm");
143                 }
144             }
145             try
146             {
147                 CMSSecureReadable secureReadable = new CMSEnvelopedHelper.CMSDigestAuthenticatedSecureReadable(digestCalculatorProvider.get(authData.getDigestAlgorithm()), encInfo.getContentType(), readable);
148 
149                 this.recipientInfoStore = CMSEnvelopedHelper.buildRecipientInformationStore(recipientInfos, this.macAlg, secureReadable, new AuthAttributesProvider()
150                 {
151                     public ASN1Set getAuthAttributes()
152                     {
153                         return authAttrs;
154                     }
155 
156                     public boolean isAead()
157                     {
158                         return false;
159                     }
160                 });
161             }
162             catch (OperatorCreationException e)
163             {
164                 throw new CMSException("unable to create digest calculator: " + e.getMessage(), e);
165             }
166         }
167         else
168         {
169             CMSSecureReadable secureReadable = new CMSEnvelopedHelper.CMSAuthenticatedSecureReadable(this.macAlg, encInfo.getContentType(), readable);
170 
171             this.recipientInfoStore = CMSEnvelopedHelper.buildRecipientInformationStore(recipientInfos, this.macAlg, secureReadable);
172         }
173     }
174 
175     /**
176      * Return the originator information associated with this message if present.
177      *
178      * @return OriginatorInformation, null if not present.
179      */
getOriginatorInfo()180     public OriginatorInformation getOriginatorInfo()
181     {
182         return originatorInfo;
183     }
184 
getMac()185     public byte[] getMac()
186     {
187         return Arrays.clone(mac);
188     }
189 
encodeObj( ASN1Encodable obj)190     private byte[] encodeObj(
191         ASN1Encodable obj)
192         throws IOException
193     {
194         if (obj != null)
195         {
196             return obj.toASN1Primitive().getEncoded();
197         }
198 
199         return null;
200     }
201 
202     /**
203      * Return the MAC algorithm details for the MAC associated with the data in this object.
204      *
205      * @return AlgorithmIdentifier representing the MAC algorithm.
206      */
getMacAlgorithm()207     public AlgorithmIdentifier getMacAlgorithm()
208     {
209         return macAlg;
210     }
211 
212     /**
213      * return the object identifier for the content MAC algorithm.
214      */
getMacAlgOID()215     public String getMacAlgOID()
216     {
217         return macAlg.getAlgorithm().getId();
218     }
219 
220     /**
221      * return the ASN.1 encoded MAC algorithm parameters, or null if
222      * there aren't any.
223      */
getMacAlgParams()224     public byte[] getMacAlgParams()
225     {
226         try
227         {
228             return encodeObj(macAlg.getParameters());
229         }
230         catch (Exception e)
231         {
232             throw new RuntimeException("exception getting encryption parameters " + e);
233         }
234     }
235 
236     /**
237      * return a store of the intended recipients for this message
238      */
getRecipientInfos()239     public RecipientInformationStore getRecipientInfos()
240     {
241         return recipientInfoStore;
242     }
243 
244     /**
245      * return the ContentInfo
246      * @deprecated use toASN1Structure()
247      */
getContentInfo()248     public ContentInfo getContentInfo()
249     {
250         return contentInfo;
251     }
252 
253     /**
254      * return the ContentInfo
255      */
toASN1Structure()256     public ContentInfo toASN1Structure()
257     {
258         return contentInfo;
259     }
260 
261     /**
262      * return a table of the digested attributes indexed by
263      * the OID of the attribute.
264      */
getAuthAttrs()265     public AttributeTable getAuthAttrs()
266     {
267         if (authAttrs == null)
268         {
269             return null;
270         }
271 
272         return new AttributeTable(authAttrs);
273     }
274 
275     /**
276      * return a table of the undigested attributes indexed by
277      * the OID of the attribute.
278      */
getUnauthAttrs()279     public AttributeTable getUnauthAttrs()
280     {
281         if (unauthAttrs == null)
282         {
283             return null;
284         }
285 
286         return new AttributeTable(unauthAttrs);
287     }
288 
289     /**
290      * return the ASN.1 encoded representation of this object.
291      */
getEncoded()292     public byte[] getEncoded()
293         throws IOException
294     {
295         return contentInfo.getEncoded();
296     }
297 
getContentDigest()298     public byte[] getContentDigest()
299     {
300         if (authAttrs != null)
301         {
302             return ASN1OctetString.getInstance(getAuthAttrs().get(CMSAttributes.messageDigest).getAttrValues().getObjectAt(0)).getOctets();
303         }
304 
305         return null;
306     }
307 }
308