• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.bouncycastle.cms;
2 
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.util.ArrayList;
7 import java.util.Collection;
8 import java.util.Collections;
9 import java.util.Enumeration;
10 import java.util.HashSet;
11 import java.util.Iterator;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 
16 import org.bouncycastle.asn1.ASN1Encodable;
17 import org.bouncycastle.asn1.ASN1EncodableVector;
18 import org.bouncycastle.asn1.ASN1InputStream;
19 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
20 import org.bouncycastle.asn1.ASN1OctetString;
21 import org.bouncycastle.asn1.ASN1Sequence;
22 import org.bouncycastle.asn1.ASN1Set;
23 import org.bouncycastle.asn1.BERSequence;
24 import org.bouncycastle.asn1.DERSet;
25 import org.bouncycastle.asn1.cms.ContentInfo;
26 import org.bouncycastle.asn1.cms.SignedData;
27 import org.bouncycastle.asn1.cms.SignerInfo;
28 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
29 import org.bouncycastle.cert.X509AttributeCertificateHolder;
30 import org.bouncycastle.cert.X509CRLHolder;
31 import org.bouncycastle.cert.X509CertificateHolder;
32 import org.bouncycastle.operator.OperatorCreationException;
33 import org.bouncycastle.util.Encodable;
34 import org.bouncycastle.util.Store;
35 
36 /**
37  * general class for handling a pkcs7-signature message.
38  *
39  * A simple example of usage - note, in the example below the validity of
40  * the certificate isn't verified, just the fact that one of the certs
41  * matches the given signer...
42  *
43  * <pre>
44  *  Store                   certStore = s.getCertificates();
45  *  SignerInformationStore  signers = s.getSignerInfos();
46  *  Collection              c = signers.getSigners();
47  *  Iterator                it = c.iterator();
48  *
49  *  while (it.hasNext())
50  *  {
51  *      SignerInformation   signer = (SignerInformation)it.next();
52  *      Collection          certCollection = certStore.getMatches(signer.getSID());
53  *
54  *      Iterator              certIt = certCollection.iterator();
55  *      X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
56  *
57  *      if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)))
58  *      {
59  *          verified++;
60  *      }
61  *  }
62  * </pre>
63  */
64 public class CMSSignedData
65     implements Encodable
66 {
67     private static final CMSSignedHelper HELPER = CMSSignedHelper.INSTANCE;
68 
69     SignedData              signedData;
70     ContentInfo             contentInfo;
71     CMSTypedData            signedContent;
72     SignerInformationStore  signerInfoStore;
73 
74     private Map             hashes;
75 
CMSSignedData( CMSSignedData c)76     private CMSSignedData(
77         CMSSignedData   c)
78     {
79         this.signedData = c.signedData;
80         this.contentInfo = c.contentInfo;
81         this.signedContent = c.signedContent;
82         this.signerInfoStore = c.signerInfoStore;
83     }
84 
CMSSignedData( byte[] sigBlock)85     public CMSSignedData(
86         byte[]      sigBlock)
87         throws CMSException
88     {
89         this(CMSUtils.readContentInfo(sigBlock));
90     }
91 
CMSSignedData( CMSProcessable signedContent, byte[] sigBlock)92     public CMSSignedData(
93         CMSProcessable  signedContent,
94         byte[]          sigBlock)
95         throws CMSException
96     {
97         this(signedContent, CMSUtils.readContentInfo(sigBlock));
98     }
99 
100     /**
101      * Content with detached signature, digests precomputed
102      *
103      * @param hashes a map of precomputed digests for content indexed by name of hash.
104      * @param sigBlock the signature object.
105      */
CMSSignedData( Map hashes, byte[] sigBlock)106     public CMSSignedData(
107         Map     hashes,
108         byte[]  sigBlock)
109         throws CMSException
110     {
111         this(hashes, CMSUtils.readContentInfo(sigBlock));
112     }
113 
114     /**
115      * base constructor - content with detached signature.
116      *
117      * @param signedContent the content that was signed.
118      * @param sigData the signature object.
119      */
CMSSignedData( CMSProcessable signedContent, InputStream sigData)120     public CMSSignedData(
121         CMSProcessable  signedContent,
122         InputStream     sigData)
123         throws CMSException
124     {
125         this(signedContent, CMSUtils.readContentInfo(new ASN1InputStream(sigData)));
126     }
127 
128     /**
129      * base constructor - with encapsulated content
130      */
CMSSignedData( InputStream sigData)131     public CMSSignedData(
132         InputStream sigData)
133         throws CMSException
134     {
135         this(CMSUtils.readContentInfo(sigData));
136     }
137 
CMSSignedData( final CMSProcessable signedContent, ContentInfo sigData)138     public CMSSignedData(
139         final CMSProcessable  signedContent,
140         ContentInfo     sigData)
141         throws CMSException
142     {
143         if (signedContent instanceof CMSTypedData)
144         {
145             this.signedContent = (CMSTypedData)signedContent;
146         }
147         else
148         {
149             this.signedContent = new CMSTypedData()
150             {
151                 public ASN1ObjectIdentifier getContentType()
152                 {
153                     return signedData.getEncapContentInfo().getContentType();
154                 }
155 
156                 public void write(OutputStream out)
157                     throws IOException, CMSException
158                 {
159                     signedContent.write(out);
160                 }
161 
162                 public Object getContent()
163                 {
164                     return signedContent.getContent();
165                 }
166             };
167         }
168 
169         this.contentInfo = sigData;
170         this.signedData = getSignedData();
171     }
172 
CMSSignedData( Map hashes, ContentInfo sigData)173     public CMSSignedData(
174         Map             hashes,
175         ContentInfo     sigData)
176         throws CMSException
177     {
178         this.hashes = hashes;
179         this.contentInfo = sigData;
180         this.signedData = getSignedData();
181     }
182 
CMSSignedData( ContentInfo sigData)183     public CMSSignedData(
184         ContentInfo sigData)
185         throws CMSException
186     {
187         this.contentInfo = sigData;
188         this.signedData = getSignedData();
189 
190         //
191         // this can happen if the signed message is sent simply to send a
192         // certificate chain.
193         //
194         ASN1Encodable content = signedData.getEncapContentInfo().getContent();
195         if (content != null)
196         {
197             if (content instanceof ASN1OctetString)
198             {
199                 this.signedContent = new CMSProcessableByteArray(signedData.getEncapContentInfo().getContentType(),
200                     ((ASN1OctetString)content).getOctets());
201             }
202             else
203             {
204                 this.signedContent = new PKCS7ProcessableObject(signedData.getEncapContentInfo().getContentType(), content);
205             }
206         }
207         else
208         {
209             this.signedContent = null;
210         }
211     }
212 
getSignedData()213     private SignedData getSignedData()
214         throws CMSException
215     {
216         try
217         {
218             return SignedData.getInstance(contentInfo.getContent());
219         }
220         catch (ClassCastException e)
221         {
222             throw new CMSException("Malformed content.", e);
223         }
224         catch (IllegalArgumentException e)
225         {
226             throw new CMSException("Malformed content.", e);
227         }
228     }
229 
230     /**
231      * Return the version number for this object
232      */
getVersion()233     public int getVersion()
234     {
235         return signedData.getVersion().getValue().intValue();
236     }
237 
238     /**
239      * return the collection of signers that are associated with the
240      * signatures for the message.
241      */
getSignerInfos()242     public SignerInformationStore getSignerInfos()
243     {
244         if (signerInfoStore == null)
245         {
246             ASN1Set         s = signedData.getSignerInfos();
247             List            signerInfos = new ArrayList();
248 
249             for (int i = 0; i != s.size(); i++)
250             {
251                 SignerInfo info = SignerInfo.getInstance(s.getObjectAt(i));
252                 ASN1ObjectIdentifier contentType = signedData.getEncapContentInfo().getContentType();
253 
254                 if (hashes == null)
255                 {
256                     signerInfos.add(new SignerInformation(info, contentType, signedContent, null));
257                 }
258                 else
259                 {
260                     Object obj = hashes.keySet().iterator().next();
261                     byte[] hash = (obj instanceof String) ? (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm().getId()) : (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm());
262 
263                     signerInfos.add(new SignerInformation(info, contentType, null, hash));
264                 }
265             }
266 
267             signerInfoStore = new SignerInformationStore(signerInfos);
268         }
269 
270         return signerInfoStore;
271     }
272 
273     /**
274      * Return if this is object represents a detached signature.
275      *
276      * @return true if this message represents a detached signature, false otherwise.
277      */
isDetachedSignature()278     public boolean isDetachedSignature()
279     {
280         return signedData.getEncapContentInfo().getContent() == null && signedData.getSignerInfos().size() > 0;
281     }
282 
283     /**
284      * Return if this is object represents a certificate management message.
285      *
286      * @return true if the message has no signers or content, false otherwise.
287      */
isCertificateManagementMessage()288     public boolean isCertificateManagementMessage()
289     {
290         return signedData.getEncapContentInfo().getContent() == null && signedData.getSignerInfos().size() == 0;
291     }
292 
293     /**
294      * Return any X.509 certificate objects in this SignedData structure as a Store of X509CertificateHolder objects.
295      *
296      * @return a Store of X509CertificateHolder objects.
297      */
getCertificates()298     public Store<X509CertificateHolder> getCertificates()
299     {
300         return HELPER.getCertificates(signedData.getCertificates());
301     }
302 
303     /**
304      * Return any X.509 CRL objects in this SignedData structure as a Store of X509CRLHolder objects.
305      *
306      * @return a Store of X509CRLHolder objects.
307      */
getCRLs()308     public Store<X509CRLHolder> getCRLs()
309     {
310         return HELPER.getCRLs(signedData.getCRLs());
311     }
312 
313     /**
314      * Return any X.509 attribute certificate objects in this SignedData structure as a Store of X509AttributeCertificateHolder objects.
315      *
316      * @return a Store of X509AttributeCertificateHolder objects.
317      */
getAttributeCertificates()318     public Store<X509AttributeCertificateHolder> getAttributeCertificates()
319     {
320         return HELPER.getAttributeCertificates(signedData.getCertificates());
321     }
322 
323     // BEGIN Android-removed: OtherRevocationInfoFormat isn't supported
324     /*
325     /**
326      * Return any OtherRevocationInfo OtherRevInfo objects of the type indicated by otherRevocationInfoFormat in
327      * this SignedData structure.
328      *
329      * @param otherRevocationInfoFormat OID of the format type been looked for.
330      *
331      * @return a Store of ASN1Encodable objects representing any objects of otherRevocationInfoFormat found.
332      *
333     public Store getOtherRevocationInfo(ASN1ObjectIdentifier otherRevocationInfoFormat)
334     {
335         return HELPER.getOtherRevocationInfo(otherRevocationInfoFormat, signedData.getCRLs());
336     }
337     */
338     // END Android-removed: OtherRevocationInfoFormat isn't supported
339 
340     /**
341      * Return the digest algorithm identifiers for the SignedData object
342      *
343      * @return the set of digest algorithm identifiers
344      */
getDigestAlgorithmIDs()345     public Set<AlgorithmIdentifier> getDigestAlgorithmIDs()
346     {
347         Set<AlgorithmIdentifier> digests = new HashSet<AlgorithmIdentifier>(signedData.getDigestAlgorithms().size());
348 
349         for (Enumeration en = signedData.getDigestAlgorithms().getObjects(); en.hasMoreElements();)
350         {
351             digests.add(AlgorithmIdentifier.getInstance(en.nextElement()));
352         }
353 
354         return Collections.unmodifiableSet(digests);
355     }
356 
357     /**
358      * Return the a string representation of the OID associated with the
359      * encapsulated content info structure carried in the signed data.
360      *
361      * @return the OID for the content type.
362      */
getSignedContentTypeOID()363     public String getSignedContentTypeOID()
364     {
365         return signedData.getEncapContentInfo().getContentType().getId();
366     }
367 
getSignedContent()368     public CMSTypedData getSignedContent()
369     {
370         return signedContent;
371     }
372 
373     /**
374      * return the ContentInfo
375      */
toASN1Structure()376     public ContentInfo toASN1Structure()
377     {
378         return contentInfo;
379     }
380 
381     /**
382      * return the ASN.1 encoded representation of this object.
383      */
getEncoded()384     public byte[] getEncoded()
385         throws IOException
386     {
387         return contentInfo.getEncoded();
388     }
389 
390     // BEGIN Android-removed: Unknown reason
391     /*
392     /**
393      * Verify all the SignerInformation objects and their associated counter signatures attached
394      * to this CMS SignedData object.
395      *
396      * @param verifierProvider  a provider of SignerInformationVerifier objects.
397      * @return true if all verify, false otherwise.
398      * @throws CMSException  if an exception occurs during the verification process.
399      *
400     public boolean verifySignatures(SignerInformationVerifierProvider verifierProvider)
401         throws CMSException
402     {
403         return verifySignatures(verifierProvider, false);
404     }
405 
406     /**
407      * Verify all the SignerInformation objects and optionally their associated counter signatures attached
408      * to this CMS SignedData object.
409      *
410      * @param verifierProvider  a provider of SignerInformationVerifier objects.
411      * @param ignoreCounterSignatures if true don't check counter signatures. If false check counter signatures as well.
412      * @return true if all verify, false otherwise.
413      * @throws CMSException  if an exception occurs during the verification process.
414      *
415     public boolean verifySignatures(SignerInformationVerifierProvider verifierProvider, boolean ignoreCounterSignatures)
416         throws CMSException
417     {
418         Collection signers = this.getSignerInfos().getSigners();
419 
420         for (Iterator it = signers.iterator(); it.hasNext();)
421         {
422             SignerInformation signer = (SignerInformation)it.next();
423 
424             try
425             {
426                 SignerInformationVerifier verifier = verifierProvider.get(signer.getSID());
427 
428                 if (!signer.verify(verifier))
429                 {
430                     return false;
431                 }
432 
433                 if (!ignoreCounterSignatures)
434                 {
435                     Collection counterSigners = signer.getCounterSignatures().getSigners();
436 
437                     for  (Iterator cIt = counterSigners.iterator(); cIt.hasNext();)
438                     {
439                         if (!verifyCounterSignature((SignerInformation)cIt.next(), verifierProvider))
440                         {
441                             return false;
442                         }
443                     }
444                 }
445             }
446             catch (OperatorCreationException e)
447             {
448                 throw new CMSException("failure in verifier provider: " + e.getMessage(), e);
449             }
450         }
451 
452         return true;
453     }
454 
455     private boolean verifyCounterSignature(SignerInformation counterSigner, SignerInformationVerifierProvider verifierProvider)
456         throws OperatorCreationException, CMSException
457     {
458         SignerInformationVerifier counterVerifier = verifierProvider.get(counterSigner.getSID());
459 
460         if (!counterSigner.verify(counterVerifier))
461         {
462             return false;
463         }
464 
465         Collection counterSigners = counterSigner.getCounterSignatures().getSigners();
466         for  (Iterator cIt = counterSigners.iterator(); cIt.hasNext();)
467         {
468             if (!verifyCounterSignature((SignerInformation)cIt.next(), verifierProvider))
469             {
470                 return false;
471             }
472         }
473 
474         return true;
475     }
476     */
477     // END Android-removed: Unknown reason
478 
479     /**
480      * Replace the SignerInformation store associated with this
481      * CMSSignedData object with the new one passed in. You would
482      * probably only want to do this if you wanted to change the unsigned
483      * attributes associated with a signer, or perhaps delete one.
484      *
485      * @param signedData the signed data object to be used as a base.
486      * @param signerInformationStore the new signer information store to use.
487      * @return a new signed data object.
488      */
replaceSigners( CMSSignedData signedData, SignerInformationStore signerInformationStore)489     public static CMSSignedData replaceSigners(
490         CMSSignedData           signedData,
491         SignerInformationStore  signerInformationStore)
492     {
493         //
494         // copy
495         //
496         CMSSignedData   cms = new CMSSignedData(signedData);
497 
498         //
499         // replace the store
500         //
501         cms.signerInfoStore = signerInformationStore;
502 
503         //
504         // replace the signers in the SignedData object
505         //
506         ASN1EncodableVector digestAlgs = new ASN1EncodableVector();
507         ASN1EncodableVector vec = new ASN1EncodableVector();
508 
509         Iterator    it = signerInformationStore.getSigners().iterator();
510         while (it.hasNext())
511         {
512             SignerInformation signer = (SignerInformation)it.next();
513             digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
514             vec.add(signer.toASN1Structure());
515         }
516 
517         ASN1Set             digests = new DERSet(digestAlgs);
518         ASN1Set             signers = new DERSet(vec);
519         ASN1Sequence        sD = (ASN1Sequence)signedData.signedData.toASN1Primitive();
520 
521         vec = new ASN1EncodableVector();
522 
523         //
524         // signers are the last item in the sequence.
525         //
526         vec.add(sD.getObjectAt(0)); // version
527         vec.add(digests);
528 
529         for (int i = 2; i != sD.size() - 1; i++)
530         {
531             vec.add(sD.getObjectAt(i));
532         }
533 
534         vec.add(signers);
535 
536         cms.signedData = SignedData.getInstance(new BERSequence(vec));
537 
538         //
539         // replace the contentInfo with the new one
540         //
541         cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData);
542 
543         return cms;
544     }
545 
546     /**
547      * Replace the certificate and CRL information associated with this
548      * CMSSignedData object with the new one passed in.
549      *
550      * @param signedData the signed data object to be used as a base.
551      * @param certificates the new certificates to be used.
552      * @param attrCerts the new attribute certificates to be used.
553      * @param revocations the new CRLs to be used - a collection of X509CRLHolder objects, OtherRevocationInfoFormat, or both.
554      * @return a new signed data object.
555      * @exception CMSException if there is an error processing the CertStore
556      */
replaceCertificatesAndCRLs( CMSSignedData signedData, Store certificates, Store attrCerts, Store revocations)557     public static CMSSignedData replaceCertificatesAndCRLs(
558         CMSSignedData   signedData,
559         Store           certificates,
560         Store           attrCerts,
561         Store           revocations)
562         throws CMSException
563     {
564         //
565         // copy
566         //
567         CMSSignedData   cms = new CMSSignedData(signedData);
568 
569         //
570         // replace the certs and revocations in the SignedData object
571         //
572         ASN1Set certSet = null;
573         ASN1Set crlSet = null;
574 
575         if (certificates != null || attrCerts != null)
576         {
577             List certs = new ArrayList();
578 
579             if (certificates != null)
580             {
581                 certs.addAll(CMSUtils.getCertificatesFromStore(certificates));
582             }
583             if (attrCerts != null)
584             {
585                 certs.addAll(CMSUtils.getAttributeCertificatesFromStore(attrCerts));
586             }
587 
588             ASN1Set set = CMSUtils.createBerSetFromList(certs);
589 
590             if (set.size() != 0)
591             {
592                 certSet = set;
593             }
594         }
595 
596         if (revocations != null)
597         {
598             ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(revocations));
599 
600             if (set.size() != 0)
601             {
602                 crlSet = set;
603             }
604         }
605 
606         //
607         // replace the CMS structure.
608         //
609         cms.signedData = new SignedData(signedData.signedData.getDigestAlgorithms(),
610                                    signedData.signedData.getEncapContentInfo(),
611                                    certSet,
612                                    crlSet,
613                                    signedData.signedData.getSignerInfos());
614 
615         //
616         // replace the contentInfo with the new one
617         //
618         cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData);
619 
620         return cms;
621     }
622 }
623