Home | History | Annotate | Download | only in cms
      1 package org.bouncycastle.cms;
      2 
      3 import java.io.ByteArrayOutputStream;
      4 import java.io.IOException;
      5 import java.io.OutputStream;
      6 import java.util.ArrayList;
      7 import java.util.Iterator;
      8 import java.util.List;
      9 
     10 import org.bouncycastle.asn1.ASN1EncodableVector;
     11 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
     12 import org.bouncycastle.asn1.ASN1OctetString;
     13 import org.bouncycastle.asn1.ASN1Set;
     14 import org.bouncycastle.asn1.BEROctetString;
     15 import org.bouncycastle.asn1.DERSet;
     16 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
     17 import org.bouncycastle.asn1.cms.ContentInfo;
     18 import org.bouncycastle.asn1.cms.SignedData;
     19 import org.bouncycastle.asn1.cms.SignerInfo;
     20 
     21 /**
     22  * general class for generating a pkcs7-signature message.
     23  * <p>
     24  * A simple example of usage, generating a detached signature.
     25  *
     26  * <pre>
     27  *      List             certList = new ArrayList();
     28  *      CMSTypedData     msg = new CMSProcessableByteArray("Hello world!".getBytes());
     29  *
     30  *      certList.add(signCert);
     31  *
     32  *      Store           certs = new JcaCertStore(certList);
     33  *
     34  *      CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
     35  *      ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(signKP.getPrivate());
     36  *
     37  *      gen.addSignerInfoGenerator(
     38  *                new JcaSignerInfoGeneratorBuilder(
     39  *                     new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
     40  *                     .build(sha1Signer, signCert));
     41  *
     42  *      gen.addCertificates(certs);
     43  *
     44  *      CMSSignedData sigData = gen.generate(msg, false);
     45  * </pre>
     46  */
     47 public class CMSSignedDataGenerator
     48     extends CMSSignedGenerator
     49 {
     50     private List signerInfs = new ArrayList();
     51 
     52     /**
     53      * base constructor
     54      */
     55     public CMSSignedDataGenerator()
     56     {
     57     }
     58 
     59     /**
     60      * Generate a CMS Signed Data object carrying a detached CMS signature.
     61      *
     62      * @param content the content to be signed.
     63      */
     64     public CMSSignedData generate(
     65         CMSTypedData content)
     66         throws CMSException
     67     {
     68         return generate(content, false);
     69     }
     70 
     71     /**
     72      * Generate a CMS Signed Data object which can be carrying a detached CMS signature, or have encapsulated data, depending on the value
     73      * of the encapsulated parameter.
     74      *
     75      * @param content the content to be signed.
     76      * @param encapsulate true if the content should be encapsulated in the signature, false otherwise.
     77      */
     78     public CMSSignedData generate(
     79         // FIXME Avoid accessing more than once to support CMSProcessableInputStream
     80         CMSTypedData content,
     81         boolean encapsulate)
     82         throws CMSException
     83     {
     84         if (!signerInfs.isEmpty())
     85         {
     86             throw new IllegalStateException("this method can only be used with SignerInfoGenerator");
     87         }
     88 
     89                 // TODO
     90 //        if (signerInfs.isEmpty())
     91 //        {
     92 //            /* RFC 3852 5.2
     93 //             * "In the degenerate case where there are no signers, the
     94 //             * EncapsulatedContentInfo value being "signed" is irrelevant.  In this
     95 //             * case, the content type within the EncapsulatedContentInfo value being
     96 //             * "signed" MUST be id-data (as defined in section 4), and the content
     97 //             * field of the EncapsulatedContentInfo value MUST be omitted."
     98 //             */
     99 //            if (encapsulate)
    100 //            {
    101 //                throw new IllegalArgumentException("no signers, encapsulate must be false");
    102 //            }
    103 //            if (!DATA.equals(eContentType))
    104 //            {
    105 //                throw new IllegalArgumentException("no signers, eContentType must be id-data");
    106 //            }
    107 //        }
    108 //
    109 //        if (!DATA.equals(eContentType))
    110 //        {
    111 //            /* RFC 3852 5.3
    112 //             * [The 'signedAttrs']...
    113 //             * field is optional, but it MUST be present if the content type of
    114 //             * the EncapsulatedContentInfo value being signed is not id-data.
    115 //             */
    116 //            // TODO signedAttrs must be present for all signers
    117 //        }
    118 
    119         ASN1EncodableVector  digestAlgs = new ASN1EncodableVector();
    120         ASN1EncodableVector  signerInfos = new ASN1EncodableVector();
    121 
    122         digests.clear();  // clear the current preserved digest state
    123 
    124         //
    125         // add the precalculated SignerInfo objects.
    126         //
    127         for (Iterator it = _signers.iterator(); it.hasNext();)
    128         {
    129             SignerInformation signer = (SignerInformation)it.next();
    130             digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
    131 
    132             // TODO Verify the content type and calculated digest match the precalculated SignerInfo
    133             signerInfos.add(signer.toASN1Structure());
    134         }
    135 
    136         //
    137         // add the SignerInfo objects
    138         //
    139         ASN1ObjectIdentifier contentTypeOID = content.getContentType();
    140 
    141         ASN1OctetString octs = null;
    142 
    143         if (content.getContent() != null)
    144         {
    145             ByteArrayOutputStream bOut = null;
    146 
    147             if (encapsulate)
    148             {
    149                 bOut = new ByteArrayOutputStream();
    150             }
    151 
    152             OutputStream cOut = CMSUtils.attachSignersToOutputStream(signerGens, bOut);
    153 
    154             // Just in case it's unencapsulated and there are no signers!
    155             cOut = CMSUtils.getSafeOutputStream(cOut);
    156 
    157             try
    158             {
    159                 content.write(cOut);
    160 
    161                 cOut.close();
    162             }
    163             catch (IOException e)
    164             {
    165                 throw new CMSException("data processing exception: " + e.getMessage(), e);
    166             }
    167 
    168             if (encapsulate)
    169             {
    170                 octs = new BEROctetString(bOut.toByteArray());
    171             }
    172         }
    173 
    174         for (Iterator it = signerGens.iterator(); it.hasNext();)
    175         {
    176             SignerInfoGenerator sGen = (SignerInfoGenerator)it.next();
    177             SignerInfo inf = sGen.generate(contentTypeOID);
    178 
    179             digestAlgs.add(inf.getDigestAlgorithm());
    180             signerInfos.add(inf);
    181 
    182             byte[] calcDigest = sGen.getCalculatedDigest();
    183 
    184             if (calcDigest != null)
    185             {
    186                 digests.put(inf.getDigestAlgorithm().getAlgorithm().getId(), calcDigest);
    187             }
    188         }
    189 
    190         ASN1Set certificates = null;
    191 
    192         if (certs.size() != 0)
    193         {
    194             certificates = CMSUtils.createBerSetFromList(certs);
    195         }
    196 
    197         ASN1Set certrevlist = null;
    198 
    199         if (crls.size() != 0)
    200         {
    201             certrevlist = CMSUtils.createBerSetFromList(crls);
    202         }
    203 
    204         ContentInfo encInfo = new ContentInfo(contentTypeOID, octs);
    205 
    206         SignedData  sd = new SignedData(
    207                                  new DERSet(digestAlgs),
    208                                  encInfo,
    209                                  certificates,
    210                                  certrevlist,
    211                                  new DERSet(signerInfos));
    212 
    213         ContentInfo contentInfo = new ContentInfo(
    214             CMSObjectIdentifiers.signedData, sd);
    215 
    216         return new CMSSignedData(content, contentInfo);
    217     }
    218 
    219     /**
    220      * generate a set of one or more SignerInformation objects representing counter signatures on
    221      * the passed in SignerInformation object.
    222      *
    223      * @param signer the signer to be countersigned
    224      * @return a store containing the signers.
    225      */
    226     public SignerInformationStore generateCounterSigners(SignerInformation signer)
    227         throws CMSException
    228     {
    229         return this.generate(new CMSProcessableByteArray(null, signer.getSignature()), false).getSignerInfos();
    230     }
    231 }
    232 
    233