diff options
Diffstat (limited to 'pkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java')
-rw-r--r-- | pkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/pkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java b/pkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java new file mode 100644 index 00000000..9692e156 --- /dev/null +++ b/pkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java @@ -0,0 +1,788 @@ +package org.bouncycastle.cms; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.BEROctetString; +import org.bouncycastle.asn1.DERSet; +import org.bouncycastle.asn1.cms.AttributeTable; +import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; +import org.bouncycastle.asn1.cms.ContentInfo; +import org.bouncycastle.asn1.cms.SignedData; +import org.bouncycastle.asn1.cms.SignerInfo; +import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.bc.BcDigestCalculatorProvider; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; + +/** + * general class for generating a pkcs7-signature message. + * <p> + * A simple example of usage, generating a detached signature. + * + * <pre> + * List certList = new ArrayList(); + * CMSTypedData msg = new CMSProcessableByteArray("Hello world!".getBytes()); + * + * certList.add(signCert); + * + * Store certs = new JcaCertStore(certList); + * + * CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); + * ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(signKP.getPrivate()); + * + * gen.addSignerInfoGenerator( + * new JcaSignerInfoGeneratorBuilder( + * new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()) + * .build(sha1Signer, signCert)); + * + * gen.addCertificates(certs); + * + * CMSSignedData sigData = gen.generate(msg, false); + * </pre> + */ +public class CMSSignedDataGenerator + extends CMSSignedGenerator +{ + private List signerInfs = new ArrayList(); + + private class SignerInf + { + final PrivateKey key; + final Object signerIdentifier; + final String digestOID; + final String encOID; + final CMSAttributeTableGenerator sAttr; + final CMSAttributeTableGenerator unsAttr; + final AttributeTable baseSignedTable; + + SignerInf( + PrivateKey key, + Object signerIdentifier, + String digestOID, + String encOID, + CMSAttributeTableGenerator sAttr, + CMSAttributeTableGenerator unsAttr, + AttributeTable baseSignedTable) + { + this.key = key; + this.signerIdentifier = signerIdentifier; + this.digestOID = digestOID; + this.encOID = encOID; + this.sAttr = sAttr; + this.unsAttr = unsAttr; + this.baseSignedTable = baseSignedTable; + } + + SignerInfoGenerator toSignerInfoGenerator( + SecureRandom random, + Provider sigProvider, + boolean addDefaultAttributes) + throws IOException, CertificateEncodingException, CMSException, OperatorCreationException, NoSuchAlgorithmException + { + String digestName = CMSSignedHelper.INSTANCE.getDigestAlgName(digestOID); + String signatureName = digestName + "with" + CMSSignedHelper.INSTANCE.getEncryptionAlgName(encOID); + + JcaSignerInfoGeneratorBuilder builder = new JcaSignerInfoGeneratorBuilder(new BcDigestCalculatorProvider()); + + if (addDefaultAttributes) + { + builder.setSignedAttributeGenerator(sAttr); + } + builder.setDirectSignature(!addDefaultAttributes); + + builder.setUnsignedAttributeGenerator(unsAttr); + + JcaContentSignerBuilder signerBuilder; + + try + { + signerBuilder = new JcaContentSignerBuilder(signatureName).setSecureRandom(random); + } + catch (IllegalArgumentException e) + { + throw new NoSuchAlgorithmException(e.getMessage()); + } + + if (sigProvider != null) + { + signerBuilder.setProvider(sigProvider); + } + + ContentSigner contentSigner = signerBuilder.build(key); + if (signerIdentifier instanceof X509Certificate) + { + return builder.build(contentSigner, (X509Certificate)signerIdentifier); + } + else + { + return builder.build(contentSigner, (byte[])signerIdentifier); + } + } + } + /** + * base constructor + */ + public CMSSignedDataGenerator() + { + } + + /** + * constructor allowing specific source of randomness + * @param rand instance of SecureRandom to use + * @deprecated rand ignored in new API, use base constructor. + */ + public CMSSignedDataGenerator( + SecureRandom rand) + { + super(rand); + } + + /** + * add a signer - no attributes other than the default ones will be + * provided here. + * + * @param key signing key to use + * @param cert certificate containing corresponding public key + * @param digestOID digest algorithm OID + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + X509Certificate cert, + String digestOID) + throws IllegalArgumentException + { + addSigner(key, cert, getEncOID(key, digestOID), digestOID); + } + + /** + * add a signer, specifying the digest encryption algorithm to use - no attributes other than the default ones will be + * provided here. + * + * @param key signing key to use + * @param cert certificate containing corresponding public key + * @param encryptionOID digest encryption algorithm OID + * @param digestOID digest algorithm OID + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + X509Certificate cert, + String encryptionOID, + String digestOID) + throws IllegalArgumentException + { + doAddSigner(key, cert, encryptionOID, digestOID, + new DefaultSignedAttributeTableGenerator(), null, null); + } + + /** + * add a signer - no attributes other than the default ones will be + * provided here. + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + byte[] subjectKeyID, + String digestOID) + throws IllegalArgumentException + { + addSigner(key, subjectKeyID, getEncOID(key, digestOID), digestOID); + } + + /** + * add a signer, specifying the digest encryption algorithm to use - no attributes other than the default ones will be + * provided here. + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + byte[] subjectKeyID, + String encryptionOID, + String digestOID) + throws IllegalArgumentException + { + doAddSigner(key, subjectKeyID, encryptionOID, digestOID, + new DefaultSignedAttributeTableGenerator(), null, null); + } + + /** + * add a signer with extra signed/unsigned attributes. + * + * @param key signing key to use + * @param cert certificate containing corresponding public key + * @param digestOID digest algorithm OID + * @param signedAttr table of attributes to be included in signature + * @param unsignedAttr table of attributes to be included as unsigned + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + X509Certificate cert, + String digestOID, + AttributeTable signedAttr, + AttributeTable unsignedAttr) + throws IllegalArgumentException + { + addSigner(key, cert, getEncOID(key, digestOID), digestOID, signedAttr, unsignedAttr); + } + + /** + * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes. + * + * @param key signing key to use + * @param cert certificate containing corresponding public key + * @param encryptionOID digest encryption algorithm OID + * @param digestOID digest algorithm OID + * @param signedAttr table of attributes to be included in signature + * @param unsignedAttr table of attributes to be included as unsigned + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + X509Certificate cert, + String encryptionOID, + String digestOID, + AttributeTable signedAttr, + AttributeTable unsignedAttr) + throws IllegalArgumentException + { + doAddSigner(key, cert, encryptionOID, digestOID, + new DefaultSignedAttributeTableGenerator(signedAttr), + new SimpleAttributeTableGenerator(unsignedAttr), signedAttr); + } + + /** + * add a signer with extra signed/unsigned attributes. + * + * @param key signing key to use + * @param subjectKeyID subjectKeyID of corresponding public key + * @param digestOID digest algorithm OID + * @param signedAttr table of attributes to be included in signature + * @param unsignedAttr table of attributes to be included as unsigned + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + byte[] subjectKeyID, + String digestOID, + AttributeTable signedAttr, + AttributeTable unsignedAttr) + throws IllegalArgumentException + { + addSigner(key, subjectKeyID, getEncOID(key, digestOID), digestOID, signedAttr, + unsignedAttr); + } + + /** + * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes. + * + * @param key signing key to use + * @param subjectKeyID subjectKeyID of corresponding public key + * @param encryptionOID digest encryption algorithm OID + * @param digestOID digest algorithm OID + * @param signedAttr table of attributes to be included in signature + * @param unsignedAttr table of attributes to be included as unsigned + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + byte[] subjectKeyID, + String encryptionOID, + String digestOID, + AttributeTable signedAttr, + AttributeTable unsignedAttr) + throws IllegalArgumentException + { + doAddSigner(key, subjectKeyID, encryptionOID, digestOID, + new DefaultSignedAttributeTableGenerator(signedAttr), + new SimpleAttributeTableGenerator(unsignedAttr), signedAttr); + } + + /** + * add a signer with extra signed/unsigned attributes based on generators. + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + X509Certificate cert, + String digestOID, + CMSAttributeTableGenerator signedAttrGen, + CMSAttributeTableGenerator unsignedAttrGen) + throws IllegalArgumentException + { + addSigner(key, cert, getEncOID(key, digestOID), digestOID, signedAttrGen, unsignedAttrGen); + } + + /** + * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes based on generators. + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + X509Certificate cert, + String encryptionOID, + String digestOID, + CMSAttributeTableGenerator signedAttrGen, + CMSAttributeTableGenerator unsignedAttrGen) + throws IllegalArgumentException + { + doAddSigner(key, cert, encryptionOID, digestOID, signedAttrGen, + unsignedAttrGen, null); + } + + /** + * add a signer with extra signed/unsigned attributes based on generators. + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + byte[] subjectKeyID, + String digestOID, + CMSAttributeTableGenerator signedAttrGen, + CMSAttributeTableGenerator unsignedAttrGen) + throws IllegalArgumentException + { + addSigner(key, subjectKeyID, getEncOID(key, digestOID), digestOID, signedAttrGen, + unsignedAttrGen); + } + + /** + * add a signer, including digest encryption algorithm, with extra signed/unsigned attributes based on generators. + * @deprecated use addSignerInfoGenerator + */ + public void addSigner( + PrivateKey key, + byte[] subjectKeyID, + String encryptionOID, + String digestOID, + CMSAttributeTableGenerator signedAttrGen, + CMSAttributeTableGenerator unsignedAttrGen) + throws IllegalArgumentException + { + doAddSigner(key, subjectKeyID, encryptionOID, digestOID, + signedAttrGen, unsignedAttrGen, null); + } + + private void doAddSigner( + PrivateKey key, + Object signerIdentifier, + String encryptionOID, + String digestOID, + CMSAttributeTableGenerator signedAttrGen, + CMSAttributeTableGenerator unsignedAttrGen, + AttributeTable baseSignedTable) + throws IllegalArgumentException + { + signerInfs.add(new SignerInf(key, signerIdentifier, digestOID, encryptionOID, + signedAttrGen, unsignedAttrGen, baseSignedTable)); + } + + /** + * generate a signed object that for a CMS Signed Data + * object using the given provider. + * @deprecated use generate() method not taking provider. + */ + public CMSSignedData generate( + CMSProcessable content, + String sigProvider) + throws NoSuchAlgorithmException, NoSuchProviderException, CMSException + { + return generate(content, CMSUtils.getProvider(sigProvider)); + } + + /** + * generate a signed object that for a CMS Signed Data + * object using the given provider. + * @deprecated use generate() method not taking provider. + */ + public CMSSignedData generate( + CMSProcessable content, + Provider sigProvider) + throws NoSuchAlgorithmException, CMSException + { + return generate(content, false, sigProvider); + } + + /** + * generate a signed object that for a CMS Signed Data + * object using the given provider - if encapsulate is true a copy + * of the message will be included in the signature. The content type + * is set according to the OID represented by the string signedContentType. + * @deprecated use generate(CMSTypedData, boolean) + */ + public CMSSignedData generate( + String eContentType, + CMSProcessable content, + boolean encapsulate, + String sigProvider) + throws NoSuchAlgorithmException, NoSuchProviderException, CMSException + { + return generate(eContentType, content, encapsulate, CMSUtils.getProvider(sigProvider), + true); + } + + /** + * generate a signed object that for a CMS Signed Data + * object using the given provider - if encapsulate is true a copy + * of the message will be included in the signature. The content type + * is set according to the OID represented by the string signedContentType. + * @deprecated use generate(CMSTypedData, boolean) + */ + public CMSSignedData generate( + String eContentType, + CMSProcessable content, + boolean encapsulate, + Provider sigProvider) + throws NoSuchAlgorithmException, CMSException + { + return generate(eContentType, content, encapsulate, sigProvider, true); + } + + /** + * Similar method to the other generate methods. The additional argument + * addDefaultAttributes indicates whether or not a default set of signed attributes + * need to be added automatically. If the argument is set to false, no + * attributes will get added at all. + * @deprecated use generate(CMSTypedData, boolean) + */ + public CMSSignedData generate( + String eContentType, + CMSProcessable content, + boolean encapsulate, + String sigProvider, + boolean addDefaultAttributes) + throws NoSuchAlgorithmException, NoSuchProviderException, CMSException + { + return generate(eContentType, content, encapsulate, CMSUtils.getProvider(sigProvider), + addDefaultAttributes); + } + + /** + * Similar method to the other generate methods. The additional argument + * addDefaultAttributes indicates whether or not a default set of signed attributes + * need to be added automatically. If the argument is set to false, no + * attributes will get added at all. + * @deprecated use setDirectSignature() on SignerInformationGenerator. + */ + public CMSSignedData generate( + String eContentType, + final CMSProcessable content, + boolean encapsulate, + Provider sigProvider, + boolean addDefaultAttributes) + throws NoSuchAlgorithmException, CMSException + { + boolean isCounterSignature = (eContentType == null); + + final ASN1ObjectIdentifier contentTypeOID = isCounterSignature + ? null + : new ASN1ObjectIdentifier(eContentType); + + for (Iterator it = signerInfs.iterator(); it.hasNext();) + { + SignerInf signer = (SignerInf)it.next(); + + try + { + signerGens.add(signer.toSignerInfoGenerator(rand, sigProvider, + addDefaultAttributes)); + } + catch (OperatorCreationException e) + { + throw new CMSException("exception creating signerInf", e); + } + catch (IOException e) + { + throw new CMSException("exception encoding attributes", e); + } + catch (CertificateEncodingException e) + { + throw new CMSException("error creating sid.", e); + } + } + + signerInfs.clear(); + + if (content != null) + { + return generate(new CMSTypedData() + { + public ASN1ObjectIdentifier getContentType() + { + return contentTypeOID; + } + + public void write(OutputStream out) + throws IOException, CMSException + { + content.write(out); + } + + public Object getContent() + { + return content.getContent(); + } + }, encapsulate); + } + else + { + return generate(new CMSAbsentContent(contentTypeOID), encapsulate); + } + } + + /** + * generate a signed object that for a CMS Signed Data + * object using the given provider - if encapsulate is true a copy + * of the message will be included in the signature with the + * default content type "data". + * @deprecated use generate(CMSTypedData, boolean) + */ + public CMSSignedData generate( + CMSProcessable content, + boolean encapsulate, + String sigProvider) + throws NoSuchAlgorithmException, NoSuchProviderException, CMSException + { + if (content instanceof CMSTypedData) + { + return this.generate(((CMSTypedData)content).getContentType().getId(), content, encapsulate, sigProvider); + } + else + { + return this.generate(DATA, content, encapsulate, sigProvider); + } + } + + /** + * generate a signed object that for a CMS Signed Data + * object using the given provider - if encapsulate is true a copy + * of the message will be included in the signature with the + * default content type "data". + * @deprecated use generate(CMSTypedData, boolean) + */ + public CMSSignedData generate( + CMSProcessable content, + boolean encapsulate, + Provider sigProvider) + throws NoSuchAlgorithmException, CMSException + { + if (content instanceof CMSTypedData) + { + return this.generate(((CMSTypedData)content).getContentType().getId(), content, encapsulate, sigProvider); + } + else + { + return this.generate(DATA, content, encapsulate, sigProvider); + } + } + + public CMSSignedData generate( + CMSTypedData content) + throws CMSException + { + return generate(content, false); + } + + public CMSSignedData generate( + // FIXME Avoid accessing more than once to support CMSProcessableInputStream + CMSTypedData content, + boolean encapsulate) + throws CMSException + { + if (!signerInfs.isEmpty()) + { + throw new IllegalStateException("this method can only be used with SignerInfoGenerator"); + } + + // TODO +// if (signerInfs.isEmpty()) +// { +// /* RFC 3852 5.2 +// * "In the degenerate case where there are no signers, the +// * EncapsulatedContentInfo value being "signed" is irrelevant. In this +// * case, the content type within the EncapsulatedContentInfo value being +// * "signed" MUST be id-data (as defined in section 4), and the content +// * field of the EncapsulatedContentInfo value MUST be omitted." +// */ +// if (encapsulate) +// { +// throw new IllegalArgumentException("no signers, encapsulate must be false"); +// } +// if (!DATA.equals(eContentType)) +// { +// throw new IllegalArgumentException("no signers, eContentType must be id-data"); +// } +// } +// +// if (!DATA.equals(eContentType)) +// { +// /* RFC 3852 5.3 +// * [The 'signedAttrs']... +// * field is optional, but it MUST be present if the content type of +// * the EncapsulatedContentInfo value being signed is not id-data. +// */ +// // TODO signedAttrs must be present for all signers +// } + + ASN1EncodableVector digestAlgs = new ASN1EncodableVector(); + ASN1EncodableVector signerInfos = new ASN1EncodableVector(); + + digests.clear(); // clear the current preserved digest state + + // + // add the precalculated SignerInfo objects. + // + for (Iterator it = _signers.iterator(); it.hasNext();) + { + SignerInformation signer = (SignerInformation)it.next(); + digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID())); + + // TODO Verify the content type and calculated digest match the precalculated SignerInfo + signerInfos.add(signer.toASN1Structure()); + } + + // + // add the SignerInfo objects + // + ASN1ObjectIdentifier contentTypeOID = content.getContentType(); + + ASN1OctetString octs = null; + + if (content != null) + { + ByteArrayOutputStream bOut = null; + + if (encapsulate) + { + bOut = new ByteArrayOutputStream(); + } + + OutputStream cOut = CMSUtils.attachSignersToOutputStream(signerGens, bOut); + + // Just in case it's unencapsulated and there are no signers! + cOut = CMSUtils.getSafeOutputStream(cOut); + + try + { + content.write(cOut); + + cOut.close(); + } + catch (IOException e) + { + throw new CMSException("data processing exception: " + e.getMessage(), e); + } + + if (encapsulate) + { + octs = new BEROctetString(bOut.toByteArray()); + } + } + + for (Iterator it = signerGens.iterator(); it.hasNext();) + { + SignerInfoGenerator sGen = (SignerInfoGenerator)it.next(); + SignerInfo inf = sGen.generate(contentTypeOID); + + digestAlgs.add(inf.getDigestAlgorithm()); + signerInfos.add(inf); + + byte[] calcDigest = sGen.getCalculatedDigest(); + + if (calcDigest != null) + { + digests.put(inf.getDigestAlgorithm().getAlgorithm().getId(), calcDigest); + } + } + + ASN1Set certificates = null; + + if (certs.size() != 0) + { + certificates = CMSUtils.createBerSetFromList(certs); + } + + ASN1Set certrevlist = null; + + if (crls.size() != 0) + { + certrevlist = CMSUtils.createBerSetFromList(crls); + } + + ContentInfo encInfo = new ContentInfo(contentTypeOID, octs); + + SignedData sd = new SignedData( + new DERSet(digestAlgs), + encInfo, + certificates, + certrevlist, + new DERSet(signerInfos)); + + ContentInfo contentInfo = new ContentInfo( + CMSObjectIdentifiers.signedData, sd); + + return new CMSSignedData(content, contentInfo); + } + + /** + * generate a set of one or more SignerInformation objects representing counter signatures on + * the passed in SignerInformation object. + * + * @param signer the signer to be countersigned + * @param sigProvider the provider to be used for counter signing. + * @return a store containing the signers. + * @deprecated use generateCounterSigners(SignerInformation) + */ + public SignerInformationStore generateCounterSigners(SignerInformation signer, Provider sigProvider) + throws NoSuchAlgorithmException, CMSException + { + return this.generate(null, new CMSProcessableByteArray(signer.getSignature()), false, sigProvider).getSignerInfos(); + } + + /** + * generate a set of one or more SignerInformation objects representing counter signatures on + * the passed in SignerInformation object. + * + * @param signer the signer to be countersigned + * @param sigProvider the provider to be used for counter signing. + * @return a store containing the signers. + * @deprecated use generateCounterSigners(SignerInformation) + */ + public SignerInformationStore generateCounterSigners(SignerInformation signer, String sigProvider) + throws NoSuchAlgorithmException, NoSuchProviderException, CMSException + { + return this.generate(null, new CMSProcessableByteArray(signer.getSignature()), false, CMSUtils.getProvider(sigProvider)).getSignerInfos(); + } + + /** + * generate a set of one or more SignerInformation objects representing counter signatures on + * the passed in SignerInformation object. + * + * @param signer the signer to be countersigned + * @return a store containing the signers. + */ + public SignerInformationStore generateCounterSigners(SignerInformation signer) + throws CMSException + { + return this.generate(new CMSProcessableByteArray(null, signer.getSignature()), false).getSignerInfos(); + } +} + |