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.ASN1OutputStream;
007: import org.bouncycastle.asn1.ASN1Sequence;
008: import org.bouncycastle.asn1.ASN1Set;
009: import org.bouncycastle.asn1.BERSequence;
010: import org.bouncycastle.asn1.DERNull;
011: import org.bouncycastle.asn1.DERObject;
012: import org.bouncycastle.asn1.DERObjectIdentifier;
013: import org.bouncycastle.asn1.DERSet;
014: import org.bouncycastle.asn1.cms.ContentInfo;
015: import org.bouncycastle.asn1.cms.SignedData;
016: import org.bouncycastle.asn1.cms.SignerInfo;
017: import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
018: import org.bouncycastle.x509.NoSuchStoreException;
019: import org.bouncycastle.x509.X509Store;
020:
021: import java.io.ByteArrayOutputStream;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.security.NoSuchAlgorithmException;
025: import java.security.NoSuchProviderException;
026: import java.security.cert.CertStore;
027: import java.security.cert.CertStoreException;
028: import java.util.ArrayList;
029: import java.util.Iterator;
030: import java.util.List;
031:
032: /**
033: * general class for handling a pkcs7-signature message.
034: *
035: * A simple example of usage - note, in the example below the validity of
036: * the certificate isn't verified, just the fact that one of the certs
037: * matches the given signer...
038: *
039: * <pre>
040: * CertStore certs = s.getCertificatesAndCRLs("Collection", "BC");
041: * SignerInformationStore signers = s.getSignerInfos();
042: * Collection c = signers.getSigners();
043: * Iterator it = c.iterator();
044: *
045: * while (it.hasNext())
046: * {
047: * SignerInformation signer = (SignerInformation)it.next();
048: * Collection certCollection = certs.getCertificates(signer.getSID());
049: *
050: * Iterator certIt = certCollection.iterator();
051: * X509Certificate cert = (X509Certificate)certIt.next();
052: *
053: * if (signer.verify(cert.getPublicKey()))
054: * {
055: * verified++;
056: * }
057: * }
058: * </pre>
059: */
060: public class CMSSignedData {
061: private static CMSSignedHelper HELPER = CMSSignedHelper.INSTANCE;
062:
063: SignedData signedData;
064: ContentInfo contentInfo;
065: CMSProcessable signedContent;
066: CertStore certStore;
067: SignerInformationStore signerInfoStore;
068: X509Store attributeStore;
069: X509Store certificateStore;
070: X509Store crlStore;
071:
072: private CMSSignedData(CMSSignedData c) {
073: this .signedData = c.signedData;
074: this .contentInfo = c.contentInfo;
075: this .signedContent = c.signedContent;
076: this .certStore = c.certStore;
077: this .signerInfoStore = c.signerInfoStore;
078: }
079:
080: public CMSSignedData(byte[] sigBlock) throws CMSException {
081: this (CMSUtils.readContentInfo(sigBlock));
082: }
083:
084: public CMSSignedData(CMSProcessable signedContent, byte[] sigBlock)
085: throws CMSException {
086: this (signedContent, CMSUtils.readContentInfo(sigBlock));
087: }
088:
089: /**
090: * base constructor
091: *
092: * @param signedContent the content that was signed.
093: * @param sigData the signature object.
094: */
095: public CMSSignedData(CMSProcessable signedContent,
096: InputStream sigData) throws CMSException {
097: this (signedContent, CMSUtils
098: .readContentInfo(new ASN1InputStream(sigData)));
099: }
100:
101: /**
102: * base constructor - with encapsulated content
103: */
104: public CMSSignedData(InputStream sigData) throws CMSException {
105: this (CMSUtils.readContentInfo(sigData));
106: }
107:
108: public CMSSignedData(CMSProcessable signedContent,
109: ContentInfo sigData) {
110: this .signedContent = signedContent;
111: this .contentInfo = sigData;
112: this .signedData = SignedData.getInstance(contentInfo
113: .getContent());
114: }
115:
116: public CMSSignedData(ContentInfo sigData) {
117: this .contentInfo = sigData;
118: this .signedData = SignedData.getInstance(contentInfo
119: .getContent());
120:
121: //
122: // this can happen if the signed message is sent simply to send a
123: // certificate chain.
124: //
125: if (signedData.getEncapContentInfo().getContent() != null) {
126: this .signedContent = new CMSProcessableByteArray(
127: ((ASN1OctetString) (signedData
128: .getEncapContentInfo().getContent()))
129: .getOctets());
130: } else {
131: this .signedContent = null;
132: }
133: }
134:
135: /**
136: * Return the version number for this object
137: */
138: public int getVersion() {
139: return signedData.getVersion().getValue().intValue();
140: }
141:
142: /**
143: * return the collection of signers that are associated with the
144: * signatures for the message.
145: */
146: public SignerInformationStore getSignerInfos() {
147: if (signerInfoStore == null) {
148: ASN1Set s = signedData.getSignerInfos();
149: List signerInfos = new ArrayList();
150:
151: for (int i = 0; i != s.size(); i++) {
152: signerInfos.add(new SignerInformation(SignerInfo
153: .getInstance(s.getObjectAt(i)), signedData
154: .getEncapContentInfo().getContentType(),
155: signedContent, null));
156: }
157:
158: signerInfoStore = new SignerInformationStore(signerInfos);
159: }
160:
161: return signerInfoStore;
162: }
163:
164: /**
165: * return a X509Store containing the attribute certificates, if any, contained
166: * in this message.
167: *
168: * @param type type of store to create
169: * @param provider provider to use
170: * @return a store of attribute certificates
171: * @exception NoSuchProviderException if the provider requested isn't available.
172: * @exception NoSuchStoreException if the store type isn't available.
173: * @exception CMSException if a general exception prevents creation of the X509Store
174: */
175: public X509Store getAttributeCertificates(String type,
176: String provider) throws NoSuchStoreException,
177: NoSuchProviderException, CMSException {
178: if (attributeStore == null) {
179: attributeStore = HELPER.createAttributeStore(type,
180: provider, signedData.getCertificates());
181: }
182:
183: return attributeStore;
184: }
185:
186: /**
187: * return a X509Store containing the public key certificates, if any, contained
188: * in this message.
189: *
190: * @param type type of store to create
191: * @param provider provider to use
192: * @return a store of public key certificates
193: * @exception NoSuchProviderException if the provider requested isn't available.
194: * @exception NoSuchStoreException if the store type isn't available.
195: * @exception CMSException if a general exception prevents creation of the X509Store
196: */
197: public X509Store getCertificates(String type, String provider)
198: throws NoSuchStoreException, NoSuchProviderException,
199: CMSException {
200: if (certificateStore == null) {
201: certificateStore = HELPER.createCertificateStore(type,
202: provider, signedData.getCertificates());
203: }
204:
205: return certificateStore;
206: }
207:
208: /**
209: * return a X509Store containing CRLs, if any, contained
210: * in this message.
211: *
212: * @param type type of store to create
213: * @param provider provider to use
214: * @return a store of CRLs
215: * @exception NoSuchProviderException if the provider requested isn't available.
216: * @exception NoSuchStoreException if the store type isn't available.
217: * @exception CMSException if a general exception prevents creation of the X509Store
218: */
219: public X509Store getCRLs(String type, String provider)
220: throws NoSuchStoreException, NoSuchProviderException,
221: CMSException {
222: if (crlStore == null) {
223: crlStore = HELPER.createCRLsStore(type, provider,
224: signedData.getCRLs());
225: }
226:
227: return crlStore;
228: }
229:
230: /**
231: * return a CertStore containing the certificates and CRLs associated with
232: * this message.
233: *
234: * @exception NoSuchProviderException if the provider requested isn't available.
235: * @exception NoSuchAlgorithmException if the cert store isn't available.
236: * @exception CMSException if a general exception prevents creation of the CertStore
237: */
238: public CertStore getCertificatesAndCRLs(String type, String provider)
239: throws NoSuchAlgorithmException, NoSuchProviderException,
240: CMSException {
241: if (certStore == null) {
242: ASN1Set certSet = signedData.getCertificates();
243: ASN1Set crlSet = signedData.getCRLs();
244:
245: certStore = HELPER.createCertStore(type, provider, certSet,
246: crlSet);
247: }
248:
249: return certStore;
250: }
251:
252: /**
253: * Return the a string representation of the OID associated with the
254: * encapsulated content info structure carried in the signed data.
255: *
256: * @return the OID for the content type.
257: */
258: public String getSignedContentTypeOID() {
259: return signedData.getEncapContentInfo().getContentType()
260: .getId();
261: }
262:
263: public CMSProcessable getSignedContent() {
264: return signedContent;
265: }
266:
267: /**
268: * return the ASN.1 encoded representation of this object.
269: */
270: public byte[] getEncoded() throws IOException {
271: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
272: ASN1OutputStream aOut = new ASN1OutputStream(bOut);
273:
274: aOut.writeObject(contentInfo);
275:
276: return bOut.toByteArray();
277: }
278:
279: /**
280: * Replace the signerinformation store associated with this
281: * CMSSignedData object with the new one passed in. You would
282: * probably only want to do this if you wanted to change the unsigned
283: * attributes associated with a signer, or perhaps delete one.
284: *
285: * @param signedData the signed data object to be used as a base.
286: * @param signerInformationStore the new signer information store to use.
287: * @return a new signed data object.
288: */
289: public static CMSSignedData replaceSigners(
290: CMSSignedData signedData,
291: SignerInformationStore signerInformationStore) {
292: //
293: // copy
294: //
295: CMSSignedData cms = new CMSSignedData(signedData);
296:
297: //
298: // replace the store
299: //
300: cms.signerInfoStore = signerInformationStore;
301:
302: //
303: // replace the signers in the SignedData object
304: //
305: ASN1EncodableVector digestAlgs = new ASN1EncodableVector();
306: ASN1EncodableVector vec = new ASN1EncodableVector();
307:
308: Iterator it = signerInformationStore.getSigners().iterator();
309: while (it.hasNext()) {
310: SignerInformation signer = (SignerInformation) it.next();
311: AlgorithmIdentifier digAlgId;
312:
313: try {
314: digAlgId = makeAlgId(signer.getDigestAlgOID(), signer
315: .getDigestAlgParams());
316: } catch (IOException e) {
317: throw new RuntimeException("encoding error.", e);
318: }
319:
320: digestAlgs.add(digAlgId);
321: vec.add(signer.toSignerInfo());
322: }
323:
324: ASN1Set digests = new DERSet(digestAlgs);
325: ASN1Set signers = new DERSet(vec);
326: ASN1Sequence sD = (ASN1Sequence) signedData.signedData
327: .getDERObject();
328:
329: vec = new ASN1EncodableVector();
330:
331: //
332: // signers are the last item in the sequence.
333: //
334: vec.add(sD.getObjectAt(0)); // version
335: vec.add(digests);
336:
337: for (int i = 2; i != sD.size() - 1; i++) {
338: vec.add(sD.getObjectAt(i));
339: }
340:
341: vec.add(signers);
342:
343: cms.signedData = SignedData.getInstance(new BERSequence(vec));
344:
345: //
346: // replace the contentInfo with the new one
347: //
348: cms.contentInfo = new ContentInfo(cms.contentInfo
349: .getContentType(), cms.signedData);
350:
351: return cms;
352: }
353:
354: /**
355: * Replace the certificate and CRL information associated with this
356: * CMSSignedData object with the new one passed in.
357: *
358: * @param signedData the signed data object to be used as a base.
359: * @param certsAndCrls the new certificates and CRLs to be used.
360: * @return a new signed data object.
361: * @exception CMSException if there is an error processing the CertStore
362: */
363: public static CMSSignedData replaceCertificatesAndCRLs(
364: CMSSignedData signedData, CertStore certsAndCrls)
365: throws CMSException {
366: //
367: // copy
368: //
369: CMSSignedData cms = new CMSSignedData(signedData);
370:
371: //
372: // replace the store
373: //
374: cms.certStore = certsAndCrls;
375:
376: //
377: // replace the certs and crls in the SignedData object
378: //
379: ASN1Set certs = null;
380: ASN1Set crls = null;
381:
382: try {
383: ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils
384: .getCertificatesFromStore(certsAndCrls));
385:
386: if (set.size() != 0) {
387: certs = set;
388: }
389: } catch (CertStoreException e) {
390: throw new CMSException(
391: "error getting certs from certStore", e);
392: }
393:
394: try {
395: ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils
396: .getCRLsFromStore(certsAndCrls));
397:
398: if (set.size() != 0) {
399: crls = set;
400: }
401: } catch (CertStoreException e) {
402: throw new CMSException("error getting crls from certStore",
403: e);
404: }
405:
406: //
407: // replace the CMS structure.
408: //
409: cms.signedData = new SignedData(signedData.signedData
410: .getDigestAlgorithms(), signedData.signedData
411: .getEncapContentInfo(), certs, crls,
412: signedData.signedData.getSignerInfos());
413:
414: //
415: // replace the contentInfo with the new one
416: //
417: cms.contentInfo = new ContentInfo(cms.contentInfo
418: .getContentType(), cms.signedData);
419:
420: return cms;
421: }
422:
423: private static DERObject makeObj(byte[] encoding)
424: throws IOException {
425: if (encoding == null) {
426: return null;
427: }
428:
429: ASN1InputStream aIn = new ASN1InputStream(encoding);
430:
431: return aIn.readObject();
432: }
433:
434: private static AlgorithmIdentifier makeAlgId(String oid,
435: byte[] params) throws IOException {
436: if (params != null) {
437: return new AlgorithmIdentifier(
438: new DERObjectIdentifier(oid), makeObj(params));
439: } else {
440: return new AlgorithmIdentifier(
441: new DERObjectIdentifier(oid), new DERNull());
442: }
443: }
444: }
|