001: package org.bouncycastle.cms;
002:
003: import org.bouncycastle.asn1.ASN1EncodableVector;
004: import org.bouncycastle.asn1.ASN1InputStream;
005: import org.bouncycastle.asn1.ASN1OctetString;
006: import org.bouncycastle.asn1.ASN1Set;
007: import org.bouncycastle.asn1.ASN1TaggedObject;
008: import org.bouncycastle.asn1.BEROctetStringGenerator;
009: import org.bouncycastle.asn1.BERSequenceGenerator;
010: import org.bouncycastle.asn1.BERTaggedObject;
011: import org.bouncycastle.asn1.DERInteger;
012: import org.bouncycastle.asn1.DERNull;
013: import org.bouncycastle.asn1.DERObject;
014: import org.bouncycastle.asn1.DERObjectIdentifier;
015: import org.bouncycastle.asn1.DEROctetString;
016: import org.bouncycastle.asn1.DEROutputStream;
017: import org.bouncycastle.asn1.DERSet;
018: import org.bouncycastle.asn1.cms.AttributeTable;
019: import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
020: import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
021: import org.bouncycastle.asn1.cms.SignerIdentifier;
022: import org.bouncycastle.asn1.cms.SignerInfo;
023: import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
024: import org.bouncycastle.asn1.x509.TBSCertificateStructure;
025:
026: import java.io.ByteArrayOutputStream;
027: import java.io.IOException;
028: import java.io.OutputStream;
029: import java.security.DigestOutputStream;
030: import java.security.InvalidKeyException;
031: import java.security.MessageDigest;
032: import java.security.NoSuchAlgorithmException;
033: import java.security.NoSuchProviderException;
034: import java.security.PrivateKey;
035: import java.security.Signature;
036: import java.security.SignatureException;
037: import java.security.cert.CertificateEncodingException;
038: import java.security.cert.X509Certificate;
039: import java.util.ArrayList;
040: import java.util.Collections;
041: import java.util.Iterator;
042: import java.util.List;
043: import java.util.Map;
044:
045: /**
046: * General class for generating a pkcs7-signature message stream.
047: * <p>
048: * A simple example of usage.
049: * </p>
050: * <pre>
051: * CertStore certs...
052: * CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
053: *
054: * gen.addSigner(privateKey, cert, CMSSignedDataStreamGenerator.DIGEST_SHA1, "BC");
055: *
056: * gen.addCertificatesAndCRLs(certs);
057: *
058: * OutputStream sigOut = gen.open(bOut);
059: *
060: * sigOut.write("Hello World!".getBytes());
061: *
062: * sigOut.close();
063: * </pre>
064: */
065: public class CMSSignedDataStreamGenerator extends CMSSignedGenerator {
066: private List _signerInfs = new ArrayList();
067: private List _messageDigests = new ArrayList();
068: private int _bufferSize;
069:
070: private class SignerInf {
071: PrivateKey _key;
072: X509Certificate _cert;
073: String _digestOID;
074: String _encOID;
075: CMSAttributeTableGenerator _sAttr;
076: CMSAttributeTableGenerator _unsAttr;
077: MessageDigest _digest;
078: Signature _signature;
079:
080: SignerInf(PrivateKey key, X509Certificate cert,
081: String digestOID, String encOID,
082: CMSAttributeTableGenerator sAttr,
083: CMSAttributeTableGenerator unsAttr,
084: MessageDigest digest, Signature signature) {
085: _key = key;
086: _cert = cert;
087: _digestOID = digestOID;
088: _encOID = encOID;
089: _sAttr = sAttr;
090: _unsAttr = unsAttr;
091: _digest = digest;
092: _signature = signature;
093: }
094:
095: PrivateKey getKey() {
096: return _key;
097: }
098:
099: X509Certificate getCertificate() {
100: return _cert;
101: }
102:
103: String getDigestAlgOID() {
104: return _digestOID;
105: }
106:
107: byte[] getDigestAlgParams() {
108: return null;
109: }
110:
111: String getEncryptionAlgOID() {
112: return _encOID;
113: }
114:
115: SignerInfo toSignerInfo(DERObjectIdentifier contentType)
116: throws IOException, SignatureException,
117: CertificateEncodingException {
118: AlgorithmIdentifier digAlgId = new AlgorithmIdentifier(
119: new DERObjectIdentifier(this .getDigestAlgOID()),
120: new DERNull());
121: AlgorithmIdentifier encAlgId = getEncAlgorithmIdentifier(this
122: .getEncryptionAlgOID());
123:
124: byte[] hash = _digest.digest();
125:
126: _digests.put(_digestOID, hash.clone());
127:
128: Map parameters = getBaseParameters(contentType, digAlgId,
129: hash);
130:
131: AttributeTable signed = (_sAttr != null) ? _sAttr
132: .getAttributes(Collections
133: .unmodifiableMap(parameters)) : null;
134:
135: ASN1Set signedAttr = getAttributeSet(signed);
136:
137: //
138: // sig must be composed from the DER encoding.
139: //
140: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
141:
142: if (signedAttr != null) {
143: DEROutputStream dOut = new DEROutputStream(bOut);
144: dOut.writeObject(signedAttr);
145: } else {
146: throw new RuntimeException(
147: "signatures without signed attributes not implemented.");
148: }
149:
150: _signature.update(bOut.toByteArray());
151:
152: ASN1OctetString encDigest = new DEROctetString(_signature
153: .sign());
154:
155: parameters = getBaseParameters(contentType, digAlgId, hash);
156: parameters.put(CMSAttributeTableGenerator.SIGNATURE,
157: encDigest.getOctets().clone());
158:
159: AttributeTable unsigned = (_unsAttr != null) ? _unsAttr
160: .getAttributes(Collections
161: .unmodifiableMap(parameters)) : null;
162:
163: ASN1Set unsignedAttr = getAttributeSet(unsigned);
164:
165: X509Certificate cert = this .getCertificate();
166: ASN1InputStream aIn = new ASN1InputStream(cert
167: .getTBSCertificate());
168: TBSCertificateStructure tbs = TBSCertificateStructure
169: .getInstance(aIn.readObject());
170: IssuerAndSerialNumber encSid = new IssuerAndSerialNumber(
171: tbs.getIssuer(), tbs.getSerialNumber().getValue());
172:
173: return new SignerInfo(new SignerIdentifier(encSid),
174: digAlgId, signedAttr, encAlgId, encDigest,
175: unsignedAttr);
176: }
177:
178: }
179:
180: /**
181: * base constructor
182: */
183: public CMSSignedDataStreamGenerator() {
184: }
185:
186: /**
187: * Set the underlying string size for encapsulated data
188: *
189: * @param bufferSize length of octet strings to buffer the data.
190: */
191: public void setBufferSize(int bufferSize) {
192: _bufferSize = bufferSize;
193: }
194:
195: /**
196: * add a signer - no attributes other than the default ones will be
197: * provided here.
198: * @throws NoSuchProviderException
199: * @throws NoSuchAlgorithmException
200: * @throws InvalidKeyException
201: */
202: public void addSigner(PrivateKey key, X509Certificate cert,
203: String digestOID, String sigProvider)
204: throws NoSuchAlgorithmException, NoSuchProviderException,
205: InvalidKeyException {
206: addSigner(key, cert, digestOID,
207: new DefaultSignedAttributeTableGenerator(),
208: (CMSAttributeTableGenerator) null, sigProvider);
209: }
210:
211: /**
212: * add a signer with extra signed/unsigned attributes.
213: * @throws NoSuchProviderException
214: * @throws NoSuchAlgorithmException
215: * @throws InvalidKeyException
216: */
217: public void addSigner(PrivateKey key, X509Certificate cert,
218: String digestOID, AttributeTable signedAttr,
219: AttributeTable unsignedAttr, String sigProvider)
220: throws NoSuchAlgorithmException, NoSuchProviderException,
221: InvalidKeyException {
222: addSigner(key, cert, digestOID,
223: new DefaultSignedAttributeTableGenerator(signedAttr),
224: new SimpleAttributeTableGenerator(unsignedAttr),
225: sigProvider);
226: }
227:
228: public void addSigner(PrivateKey key, X509Certificate cert,
229: String digestOID,
230: CMSAttributeTableGenerator signedAttrGenerator,
231: CMSAttributeTableGenerator unsignedAttrGenerator,
232: String sigProvider) throws NoSuchAlgorithmException,
233: NoSuchProviderException, InvalidKeyException {
234: String encOID = getEncOID(key, digestOID);
235: String digestName = CMSSignedHelper.INSTANCE
236: .getDigestAlgName(digestOID);
237: String signatureName = digestName + "with"
238: + CMSSignedHelper.INSTANCE.getEncryptionAlgName(encOID);
239: Signature sig = CMSSignedHelper.INSTANCE.getSignatureInstance(
240: signatureName, sigProvider);
241: MessageDigest dig = CMSSignedHelper.INSTANCE.getDigestInstance(
242: digestName, sigProvider);
243:
244: sig.initSign(key);
245:
246: _signerInfs.add(new SignerInf(key, cert, digestOID, encOID,
247: signedAttrGenerator, unsignedAttrGenerator, dig, sig));
248: _messageDigests.add(dig);
249: }
250:
251: private DERObject makeObj(byte[] encoding) throws IOException {
252: if (encoding == null) {
253: return null;
254: }
255:
256: ASN1InputStream aIn = new ASN1InputStream(encoding);
257:
258: return aIn.readObject();
259: }
260:
261: private AlgorithmIdentifier makeAlgId(String oid, byte[] params)
262: throws IOException {
263: if (params != null) {
264: return new AlgorithmIdentifier(
265: new DERObjectIdentifier(oid), makeObj(params));
266: } else {
267: return new AlgorithmIdentifier(
268: new DERObjectIdentifier(oid), new DERNull());
269: }
270: }
271:
272: /**
273: * generate a signed object that for a CMS Signed Data
274: * object using the given provider.
275: */
276: public OutputStream open(OutputStream out) throws IOException {
277: return open(out, false);
278: }
279:
280: /**
281: * generate a signed object that for a CMS Signed Data
282: * object using the given provider - if encapsulate is true a copy
283: * of the message will be included in the signature with the
284: * default content type "data".
285: */
286: public OutputStream open(OutputStream out, boolean encapsulate)
287: throws IOException {
288: return open(out, DATA, encapsulate);
289: }
290:
291: /**
292: * generate a signed object that for a CMS Signed Data
293: * object using the given provider - if encapsulate is true a copy
294: * of the message will be included in the signature. The content type
295: * is set according to the OID represented by the string signedContentType.
296: */
297: public OutputStream open(OutputStream out,
298: String signedContentType, boolean encapsulate)
299: throws IOException {
300: //
301: // ContentInfo
302: //
303: BERSequenceGenerator sGen = new BERSequenceGenerator(out);
304:
305: sGen.addObject(CMSObjectIdentifiers.signedData);
306:
307: //
308: // Signed Data
309: //
310: BERSequenceGenerator sigGen = new BERSequenceGenerator(sGen
311: .getRawOutputStream(), 0, true);
312:
313: sigGen.addObject(calculateVersion(signedContentType));
314:
315: ASN1EncodableVector digestAlgs = new ASN1EncodableVector();
316:
317: //
318: // add the precalculated SignerInfo digest algorithms.
319: //
320: for (Iterator it = _signers.iterator(); it.hasNext();) {
321: SignerInformation signer = (SignerInformation) it.next();
322: AlgorithmIdentifier digAlgId;
323:
324: digAlgId = makeAlgId(signer.getDigestAlgOID(), signer
325: .getDigestAlgParams());
326:
327: digestAlgs.add(digAlgId);
328: }
329:
330: //
331: // add the new digests
332: //
333: for (Iterator it = _signerInfs.iterator(); it.hasNext();) {
334: SignerInf signer = (SignerInf) it.next();
335: AlgorithmIdentifier digAlgId;
336:
337: digAlgId = makeAlgId(signer.getDigestAlgOID(), signer
338: .getDigestAlgParams());
339:
340: digestAlgs.add(digAlgId);
341: }
342:
343: sigGen.getRawOutputStream().write(
344: new DERSet(digestAlgs).getEncoded());
345:
346: BERSequenceGenerator eiGen = new BERSequenceGenerator(sigGen
347: .getRawOutputStream());
348:
349: eiGen.addObject(new DERObjectIdentifier(signedContentType));
350:
351: OutputStream digStream;
352:
353: if (encapsulate) {
354: BEROctetStringGenerator octGen = new BEROctetStringGenerator(
355: eiGen.getRawOutputStream(), 0, true);
356:
357: if (_bufferSize != 0) {
358: digStream = octGen
359: .getOctetOutputStream(new byte[_bufferSize]);
360: } else {
361: digStream = octGen.getOctetOutputStream();
362: }
363: } else {
364: digStream = new NullOutputStream();
365: }
366:
367: for (Iterator it = _messageDigests.iterator(); it.hasNext();) {
368: digStream = new DigestOutputStream(digStream,
369: (MessageDigest) it.next());
370: }
371:
372: return new CmsSignedDataOutputStream(digStream,
373: signedContentType, sGen, sigGen, eiGen);
374: }
375:
376: // RFC3852, section 5.1:
377: // IF ((certificates is present) AND
378: // (any certificates with a type of other are present)) OR
379: // ((crls is present) AND
380: // (any crls with a type of other are present))
381: // THEN version MUST be 5
382: // ELSE
383: // IF (certificates is present) AND
384: // (any version 2 attribute certificates are present)
385: // THEN version MUST be 4
386: // ELSE
387: // IF ((certificates is present) AND
388: // (any version 1 attribute certificates are present)) OR
389: // (any SignerInfo structures are version 3) OR
390: // (encapContentInfo eContentType is other than id-data)
391: // THEN version MUST be 3
392: // ELSE version MUST be 1
393: //
394: private DERInteger calculateVersion(String contentOid) {
395: boolean otherCert = false;
396: boolean otherCrl = false;
397: boolean attrCertV1Found = false;
398: boolean attrCertV2Found = false;
399:
400: if (_certs != null) {
401: for (Iterator it = _certs.iterator(); it.hasNext();) {
402: Object obj = it.next();
403: if (obj instanceof ASN1TaggedObject) {
404: ASN1TaggedObject tagged = (ASN1TaggedObject) obj;
405:
406: if (tagged.getTagNo() == 1) {
407: attrCertV1Found = true;
408: } else if (tagged.getTagNo() == 2) {
409: attrCertV2Found = true;
410: } else if (tagged.getTagNo() == 3) {
411: otherCert = true;
412: }
413: }
414: }
415: }
416:
417: if (otherCert) {
418: return new DERInteger(5);
419: }
420:
421: if (_crls != null && !otherCert) // no need to check if otherCert is true
422: {
423: for (Iterator it = _crls.iterator(); it.hasNext();) {
424: Object obj = it.next();
425: if (obj instanceof ASN1TaggedObject) {
426: otherCrl = true;
427: }
428: }
429: }
430:
431: if (otherCrl) {
432: return new DERInteger(5);
433: }
434:
435: if (attrCertV2Found) {
436: return new DERInteger(4);
437: }
438:
439: if (attrCertV1Found) {
440: return new DERInteger(3);
441: }
442:
443: if (contentOid.equals(DATA)) {
444: if (checkForVersion3(_signers)) {
445: return new DERInteger(3);
446: } else {
447: return new DERInteger(1);
448: }
449: } else {
450: return new DERInteger(3);
451: }
452: }
453:
454: private boolean checkForVersion3(List signerInfos) {
455: for (Iterator it = signerInfos.iterator(); it.hasNext();) {
456: SignerInfo s = SignerInfo
457: .getInstance(((SignerInformation) it.next())
458: .toSignerInfo());
459:
460: if (s.getVersion().getValue().intValue() == 3) {
461: return true;
462: }
463: }
464:
465: return false;
466: }
467:
468: private class NullOutputStream extends OutputStream {
469: public void write(int b) throws IOException {
470: // do nothing
471: }
472: }
473:
474: private class CmsSignedDataOutputStream extends OutputStream {
475: private OutputStream _out;
476: private DERObjectIdentifier _contentOID;
477: private BERSequenceGenerator _sGen;
478: private BERSequenceGenerator _sigGen;
479: private BERSequenceGenerator _eiGen;
480:
481: public CmsSignedDataOutputStream(OutputStream out,
482: String contentOID, BERSequenceGenerator sGen,
483: BERSequenceGenerator sigGen, BERSequenceGenerator eiGen) {
484: _out = out;
485: _contentOID = new DERObjectIdentifier(contentOID);
486: _sGen = sGen;
487: _sigGen = sigGen;
488: _eiGen = eiGen;
489: }
490:
491: public void write(int b) throws IOException {
492: _out.write(b);
493: }
494:
495: public void write(byte[] bytes, int off, int len)
496: throws IOException {
497: _out.write(bytes, off, len);
498: }
499:
500: public void write(byte[] bytes) throws IOException {
501: _out.write(bytes);
502: }
503:
504: public void close() throws IOException {
505: _out.close();
506: _eiGen.close();
507:
508: _digests.clear(); // clear the current preserved digest state
509:
510: if (_certs.size() != 0) {
511: ASN1Set certs = CMSUtils.createBerSetFromList(_certs);
512:
513: _sigGen.getRawOutputStream().write(
514: new BERTaggedObject(false, 0, certs)
515: .getEncoded());
516: }
517:
518: if (_crls.size() != 0) {
519: ASN1Set crls = CMSUtils.createBerSetFromList(_crls);
520:
521: _sigGen.getRawOutputStream().write(
522: new BERTaggedObject(false, 1, crls)
523: .getEncoded());
524: }
525:
526: //
527: // add the precalculated SignerInfo objects.
528: //
529: ASN1EncodableVector signerInfos = new ASN1EncodableVector();
530: Iterator it = _signers.iterator();
531:
532: while (it.hasNext()) {
533: SignerInformation signer = (SignerInformation) it
534: .next();
535:
536: signerInfos.add(signer.toSignerInfo());
537: }
538:
539: //
540: // add the SignerInfo objects
541: //
542: it = _signerInfs.iterator();
543:
544: while (it.hasNext()) {
545: SignerInf signer = (SignerInf) it.next();
546:
547: try {
548: signerInfos.add(signer.toSignerInfo(_contentOID));
549: } catch (IOException e) {
550: throw new IOException("encoding error." + e);
551: } catch (SignatureException e) {
552: throw new IOException("error creating signature."
553: + e);
554: } catch (CertificateEncodingException e) {
555: throw new IOException("error creating sid." + e);
556: }
557: }
558:
559: _sigGen.getRawOutputStream().write(
560: new DERSet(signerInfos).getEncoded());
561:
562: _sigGen.close();
563: _sGen.close();
564: }
565: }
566: }
|