1package org.bouncycastle.cms; 2 3import java.io.ByteArrayOutputStream; 4import java.io.IOException; 5import java.io.OutputStream; 6import java.util.ArrayList; 7import java.util.Iterator; 8import java.util.List; 9 10import org.bouncycastle.asn1.ASN1EncodableVector; 11import org.bouncycastle.asn1.ASN1ObjectIdentifier; 12import org.bouncycastle.asn1.ASN1OctetString; 13import org.bouncycastle.asn1.ASN1Set; 14import org.bouncycastle.asn1.BEROctetString; 15import org.bouncycastle.asn1.DERSet; 16import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; 17import org.bouncycastle.asn1.cms.ContentInfo; 18import org.bouncycastle.asn1.cms.SignedData; 19import 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 */ 47public 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 != 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