001: package org.bouncycastle.jce;
002:
003: import org.bouncycastle.asn1.ASN1EncodableVector;
004: import org.bouncycastle.asn1.ASN1InputStream;
005: import org.bouncycastle.asn1.ASN1Sequence;
006: import org.bouncycastle.asn1.ASN1Set;
007: import org.bouncycastle.asn1.DERInteger;
008: import org.bouncycastle.asn1.DERNull;
009: import org.bouncycastle.asn1.DERObject;
010: import org.bouncycastle.asn1.DERObjectIdentifier;
011: import org.bouncycastle.asn1.DEROctetString;
012: import org.bouncycastle.asn1.DEROutputStream;
013: import org.bouncycastle.asn1.DERSequence;
014: import org.bouncycastle.asn1.DERSet;
015: import org.bouncycastle.asn1.DERTaggedObject;
016: import org.bouncycastle.asn1.pkcs.ContentInfo;
017: import org.bouncycastle.asn1.pkcs.IssuerAndSerialNumber;
018: import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
019: import org.bouncycastle.asn1.pkcs.SignedData;
020: import org.bouncycastle.asn1.pkcs.SignerInfo;
021: import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
022: import org.bouncycastle.asn1.x509.CertificateList;
023: import org.bouncycastle.asn1.x509.X509CertificateStructure;
024: import org.bouncycastle.asn1.x509.X509Name;
025: import org.bouncycastle.jce.provider.X509CRLObject;
026: import org.bouncycastle.jce.provider.X509CertificateObject;
027:
028: import java.io.ByteArrayInputStream;
029: import java.io.ByteArrayOutputStream;
030: import java.io.IOException;
031: import java.math.BigInteger;
032: import java.security.InvalidKeyException;
033: import java.security.NoSuchAlgorithmException;
034: import java.security.NoSuchProviderException;
035: import java.security.PrivateKey;
036: import java.security.Signature;
037: import java.security.SignatureException;
038: import java.security.cert.CRL;
039: import java.security.cert.CRLException;
040: import java.security.cert.Certificate;
041: import java.security.cert.CertificateParsingException;
042: import java.security.cert.X509CRL;
043: import java.security.cert.X509Certificate;
044: import java.util.ArrayList;
045: import java.util.Collection;
046: import java.util.Enumeration;
047: import java.util.HashSet;
048: import java.util.Iterator;
049: import java.util.Set;
050:
051: /**
052: * Represents a PKCS#7 object - specifically the "Signed Data"
053: * type.
054: * <p>
055: * How to use it? To verify a signature, do:
056: * <pre>
057: * PKCS7SignedData pkcs7 = new PKCS7SignedData(der_bytes); // Create it
058: * pkcs7.update(bytes, 0, bytes.length); // Update checksum
059: * boolean verified = pkcs7.verify(); // Does it add up?
060: *
061: * To sign, do this:
062: * PKCS7SignedData pkcs7 = new PKCS7SignedData(privKey, certChain, "MD5");
063: * pkcs7.update(bytes, 0, bytes.length); // Update checksum
064: * pkcs7.sign(); // Create digest
065: *
066: * bytes = pkcs7.getEncoded(); // Write it somewhere
067: * </pre>
068: * <p>
069: * This class is pretty close to obsolete, for a much better (and more complete)
070: * implementation of PKCS7 have a look at the org.bouncycastle.cms package.
071: * @deprecated this class really is obsolete - use the CMS package.
072: */
073: public class PKCS7SignedData implements PKCSObjectIdentifiers {
074: private int version, signerversion;
075: private Set digestalgos;
076: private Collection certs, crls;
077: private X509Certificate signCert;
078: private byte[] digest;
079: private String digestAlgorithm, digestEncryptionAlgorithm;
080: private Signature sig;
081: private transient PrivateKey privKey;
082:
083: private final String ID_PKCS7_DATA = "1.2.840.113549.1.7.1";
084: private final String ID_PKCS7_SIGNED_DATA = "1.2.840.113549.1.7.2";
085: private final String ID_MD5 = "1.2.840.113549.2.5";
086: private final String ID_MD2 = "1.2.840.113549.2.2";
087: private final String ID_SHA1 = "1.3.14.3.2.26";
088: private final String ID_RSA = "1.2.840.113549.1.1.1";
089: private final String ID_DSA = "1.2.840.10040.4.1";
090:
091: /**
092: * Read an existing PKCS#7 object from a DER encoded byte array using
093: * the BC provider.
094: */
095: public PKCS7SignedData(byte[] in) throws SecurityException,
096: CRLException, InvalidKeyException, NoSuchProviderException,
097: NoSuchAlgorithmException {
098: this (in, "BC");
099: }
100:
101: /**
102: * Read an existing PKCS#7 object from a DER encoded byte array
103: */
104: public PKCS7SignedData(byte[] in, String provider)
105: throws SecurityException, CRLException,
106: InvalidKeyException, NoSuchProviderException,
107: NoSuchAlgorithmException {
108: ASN1InputStream din = new ASN1InputStream(
109: new ByteArrayInputStream(in));
110:
111: //
112: // Basic checks to make sure it's a PKCS#7 SignedData Object
113: //
114: DERObject pkcs;
115:
116: try {
117: pkcs = din.readObject();
118: } catch (IOException e) {
119: throw new SecurityException(
120: "can't decode PKCS7SignedData object");
121: }
122:
123: if (!(pkcs instanceof ASN1Sequence)) {
124: throw new SecurityException(
125: "Not a valid PKCS#7 object - not a sequence");
126: }
127:
128: ContentInfo content = ContentInfo.getInstance(pkcs);
129:
130: if (!content.getContentType().equals(signedData)) {
131: throw new SecurityException(
132: "Not a valid PKCS#7 signed-data object - wrong header "
133: + content.getContentType().getId());
134: }
135:
136: SignedData data = SignedData.getInstance(content.getContent());
137:
138: certs = new ArrayList();
139:
140: if (data.getCertificates() != null) {
141: Enumeration ec = ASN1Set
142: .getInstance(data.getCertificates()).getObjects();
143:
144: while (ec.hasMoreElements()) {
145: try {
146: certs.add(new X509CertificateObject(
147: X509CertificateStructure.getInstance(ec
148: .nextElement())));
149: } catch (CertificateParsingException e) {
150: throw new SecurityException(e.toString());
151: }
152: }
153: }
154:
155: crls = new ArrayList();
156:
157: if (data.getCRLs() != null) {
158: Enumeration ec = ASN1Set.getInstance(data.getCRLs())
159: .getObjects();
160: while (ec.hasMoreElements()) {
161: crls.add(new X509CRLObject(CertificateList
162: .getInstance(ec.nextElement())));
163: }
164: }
165:
166: version = data.getVersion().getValue().intValue();
167:
168: //
169: // Get the digest algorithm
170: //
171: digestalgos = new HashSet();
172: Enumeration e = data.getDigestAlgorithms().getObjects();
173:
174: while (e.hasMoreElements()) {
175: ASN1Sequence s = (ASN1Sequence) e.nextElement();
176: DERObjectIdentifier o = (DERObjectIdentifier) s
177: .getObjectAt(0);
178: digestalgos.add(o.getId());
179: }
180:
181: //
182: // Get the SignerInfo
183: //
184: ASN1Set signerinfos = data.getSignerInfos();
185: if (signerinfos.size() != 1) {
186: throw new SecurityException(
187: "This PKCS#7 object has multiple SignerInfos - only one is supported at this time");
188: }
189:
190: SignerInfo signerInfo = SignerInfo.getInstance(signerinfos
191: .getObjectAt(0));
192:
193: signerversion = signerInfo.getVersion().getValue().intValue();
194:
195: IssuerAndSerialNumber isAnds = signerInfo
196: .getIssuerAndSerialNumber();
197:
198: //
199: // Get the signing certificate
200: //
201: BigInteger serialNumber = isAnds.getCertificateSerialNumber()
202: .getValue();
203: X509Principal issuer = new X509Principal(isAnds.getName());
204:
205: for (Iterator i = certs.iterator(); i.hasNext();) {
206: X509Certificate cert = (X509Certificate) i.next();
207: if (serialNumber.equals(cert.getSerialNumber())
208: && issuer.equals(cert.getIssuerDN())) {
209: signCert = cert;
210: break;
211: }
212: }
213:
214: if (signCert == null) {
215: throw new SecurityException(
216: "Can't find signing certificate with serial "
217: + serialNumber.toString(16));
218: }
219:
220: digestAlgorithm = signerInfo.getDigestAlgorithm().getObjectId()
221: .getId();
222:
223: digest = signerInfo.getEncryptedDigest().getOctets();
224: digestEncryptionAlgorithm = signerInfo
225: .getDigestEncryptionAlgorithm().getObjectId().getId();
226:
227: sig = Signature.getInstance(getDigestAlgorithm(), provider);
228:
229: sig.initVerify(signCert.getPublicKey());
230: }
231:
232: /**
233: * Create a new PKCS#7 object from the specified key using the BC provider.
234: *
235: * @param privKey the private key to be used for signing.
236: * @param certChain the certificate chain associated with the private key.
237: * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA"
238: */
239: public PKCS7SignedData(PrivateKey privKey, Certificate[] certChain,
240: String hashAlgorithm) throws SecurityException,
241: InvalidKeyException, NoSuchProviderException,
242: NoSuchAlgorithmException {
243: this (privKey, certChain, hashAlgorithm, "BC");
244: }
245:
246: /**
247: * Create a new PKCS#7 object from the specified key.
248: *
249: * @param privKey the private key to be used for signing.
250: * @param certChain the certificate chain associated with the private key.
251: * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA"
252: * @param provider the provider to use.
253: */
254: public PKCS7SignedData(PrivateKey privKey, Certificate[] certChain,
255: String hashAlgorithm, String provider)
256: throws SecurityException, InvalidKeyException,
257: NoSuchProviderException, NoSuchAlgorithmException {
258: this (privKey, certChain, null, hashAlgorithm, provider);
259: }
260:
261: /**
262: * Create a new PKCS#7 object from the specified key.
263: *
264: * @param privKey the private key to be used for signing.
265: * @param certChain the certificate chain associated with the private key.
266: * @param crlList the crl list associated with the private key.
267: * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA"
268: * @param provider the provider to use.
269: */
270: public PKCS7SignedData(PrivateKey privKey, Certificate[] certChain,
271: CRL[] crlList, String hashAlgorithm, String provider)
272: throws SecurityException, InvalidKeyException,
273: NoSuchProviderException, NoSuchAlgorithmException {
274: this .privKey = privKey;
275:
276: if (hashAlgorithm.equals("MD5")) {
277: digestAlgorithm = ID_MD5;
278: } else if (hashAlgorithm.equals("MD2")) {
279: digestAlgorithm = ID_MD2;
280: } else if (hashAlgorithm.equals("SHA")) {
281: digestAlgorithm = ID_SHA1;
282: } else if (hashAlgorithm.equals("SHA1")) {
283: digestAlgorithm = ID_SHA1;
284: } else {
285: throw new NoSuchAlgorithmException(
286: "Unknown Hash Algorithm " + hashAlgorithm);
287: }
288:
289: version = signerversion = 1;
290: certs = new ArrayList();
291: crls = new ArrayList();
292: digestalgos = new HashSet();
293: digestalgos.add(digestAlgorithm);
294:
295: //
296: // Copy in the certificates and crls used to sign the private key.
297: //
298: signCert = (X509Certificate) certChain[0];
299: for (int i = 0; i < certChain.length; i++) {
300: certs.add(certChain[i]);
301: }
302:
303: if (crlList != null) {
304: for (int i = 0; i < crlList.length; i++) {
305: crls.add(crlList[i]);
306: }
307: }
308:
309: //
310: // Now we have private key, find out what the digestEncryptionAlgorithm is.
311: //
312: digestEncryptionAlgorithm = privKey.getAlgorithm();
313: if (digestEncryptionAlgorithm.equals("RSA")) {
314: digestEncryptionAlgorithm = ID_RSA;
315: } else if (digestEncryptionAlgorithm.equals("DSA")) {
316: digestEncryptionAlgorithm = ID_DSA;
317: } else {
318: throw new NoSuchAlgorithmException("Unknown Key Algorithm "
319: + digestEncryptionAlgorithm);
320: }
321:
322: sig = Signature.getInstance(getDigestAlgorithm(), provider);
323:
324: sig.initSign(privKey);
325: }
326:
327: /**
328: * Get the algorithm used to calculate the message digest
329: */
330: public String getDigestAlgorithm() {
331: String da = digestAlgorithm;
332: String dea = digestEncryptionAlgorithm;
333:
334: if (digestAlgorithm.equals(ID_MD5)) {
335: da = "MD5";
336: } else if (digestAlgorithm.equals(ID_MD2)) {
337: da = "MD2";
338: } else if (digestAlgorithm.equals(ID_SHA1)) {
339: da = "SHA1";
340: }
341:
342: if (digestEncryptionAlgorithm.equals(ID_RSA)) {
343: dea = "RSA";
344: } else if (digestEncryptionAlgorithm.equals(ID_DSA)) {
345: dea = "DSA";
346: }
347:
348: return da + "with" + dea;
349: }
350:
351: /**
352: * Resets the PKCS7SignedData object to it's initial state, ready
353: * to sign or verify a new buffer.
354: */
355: public void reset() {
356: try {
357: if (privKey == null) {
358: sig.initVerify(signCert.getPublicKey());
359: } else {
360: sig.initSign(privKey);
361: }
362: } catch (Exception e) {
363: throw new RuntimeException(e.toString());
364: }
365: }
366:
367: /**
368: * Get the X.509 certificates associated with this PKCS#7 object
369: */
370: public Certificate[] getCertificates() {
371: return (X509Certificate[]) certs
372: .toArray(new X509Certificate[certs.size()]);
373: }
374:
375: /**
376: * Get the X.509 certificate revocation lists associated with this PKCS#7 object
377: */
378: public Collection getCRLs() {
379: return crls;
380: }
381:
382: /**
383: * Get the X.509 certificate actually used to sign the digest.
384: */
385: public X509Certificate getSigningCertificate() {
386: return signCert;
387: }
388:
389: /**
390: * Get the version of the PKCS#7 object. Always 1
391: */
392: public int getVersion() {
393: return version;
394: }
395:
396: /**
397: * Get the version of the PKCS#7 "SignerInfo" object. Always 1
398: */
399: public int getSigningInfoVersion() {
400: return signerversion;
401: }
402:
403: /**
404: * Update the digest with the specified byte. This method is used both for signing and verifying
405: */
406: public void update(byte buf) throws SignatureException {
407: sig.update(buf);
408: }
409:
410: /**
411: * Update the digest with the specified bytes. This method is used both for signing and verifying
412: */
413: public void update(byte[] buf, int off, int len)
414: throws SignatureException {
415: sig.update(buf, off, len);
416: }
417:
418: /**
419: * Verify the digest
420: */
421: public boolean verify() throws SignatureException {
422: return sig.verify(digest);
423: }
424:
425: /**
426: * Get the "issuer" from the TBSCertificate bytes that are passed in
427: */
428: private DERObject getIssuer(byte[] enc) {
429: try {
430: ASN1InputStream in = new ASN1InputStream(
431: new ByteArrayInputStream(enc));
432: ASN1Sequence seq = (ASN1Sequence) in.readObject();
433: return (DERObject) seq
434: .getObjectAt(seq.getObjectAt(0) instanceof DERTaggedObject ? 3
435: : 2);
436: } catch (IOException e) {
437: throw new Error("IOException reading from ByteArray: " + e);
438: }
439: }
440:
441: /**
442: * return the bytes for the PKCS7SignedData object.
443: */
444: public byte[] getEncoded() {
445: try {
446:
447: digest = sig.sign();
448:
449: // Create the set of Hash algorithms. I've assumed this is the
450: // set of all hash agorithms used to created the digest in the
451: // "signerInfo" structure. I may be wrong.
452: //
453: ASN1EncodableVector v = new ASN1EncodableVector();
454: for (Iterator i = digestalgos.iterator(); i.hasNext();) {
455: AlgorithmIdentifier a = new AlgorithmIdentifier(
456: new DERObjectIdentifier((String) i.next()),
457: null);
458:
459: v.add(a);
460: }
461:
462: DERSet algos = new DERSet(v);
463:
464: // Create the contentInfo. Empty, I didn't implement this bit
465: //
466: DERSequence contentinfo = new DERSequence(
467: new DERObjectIdentifier(ID_PKCS7_DATA));
468:
469: // Get all the certificates
470: //
471: v = new ASN1EncodableVector();
472: for (Iterator i = certs.iterator(); i.hasNext();) {
473: ASN1InputStream tempstream = new ASN1InputStream(
474: new ByteArrayInputStream(((X509Certificate) i
475: .next()).getEncoded()));
476: v.add(tempstream.readObject());
477: }
478:
479: DERSet dercertificates = new DERSet(v);
480:
481: // Create signerinfo structure.
482: //
483: ASN1EncodableVector signerinfo = new ASN1EncodableVector();
484:
485: // Add the signerInfo version
486: //
487: signerinfo.add(new DERInteger(signerversion));
488:
489: IssuerAndSerialNumber isAnds = new IssuerAndSerialNumber(
490: new X509Name((ASN1Sequence) getIssuer(signCert
491: .getTBSCertificate())), new DERInteger(
492: signCert.getSerialNumber()));
493: signerinfo.add(isAnds);
494:
495: // Add the digestAlgorithm
496: //
497: signerinfo.add(new AlgorithmIdentifier(
498: new DERObjectIdentifier(digestAlgorithm),
499: new DERNull()));
500:
501: //
502: // Add the digestEncryptionAlgorithm
503: //
504: signerinfo.add(new AlgorithmIdentifier(
505: new DERObjectIdentifier(digestEncryptionAlgorithm),
506: new DERNull()));
507:
508: //
509: // Add the digest
510: //
511: signerinfo.add(new DEROctetString(digest));
512:
513: //
514: // Finally build the body out of all the components above
515: //
516: ASN1EncodableVector body = new ASN1EncodableVector();
517: body.add(new DERInteger(version));
518: body.add(algos);
519: body.add(contentinfo);
520: body.add(new DERTaggedObject(false, 0, dercertificates));
521:
522: if (crls.size() > 0) {
523: v = new ASN1EncodableVector();
524: for (Iterator i = crls.iterator(); i.hasNext();) {
525: ASN1InputStream t = new ASN1InputStream(
526: new ByteArrayInputStream(((X509CRL) i
527: .next()).getEncoded()));
528: v.add(t.readObject());
529: }
530: DERSet dercrls = new DERSet(v);
531: body.add(new DERTaggedObject(false, 1, dercrls));
532: }
533:
534: // Only allow one signerInfo
535: //
536: body.add(new DERSet(new DERSequence(signerinfo)));
537:
538: // Now we have the body, wrap it in it's PKCS7Signed shell
539: // and return it
540: //
541: ASN1EncodableVector whole = new ASN1EncodableVector();
542: whole.add(new DERObjectIdentifier(ID_PKCS7_SIGNED_DATA));
543: whole.add(new DERTaggedObject(0, new DERSequence(body)));
544:
545: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
546:
547: DEROutputStream dout = new DEROutputStream(bOut);
548: dout.writeObject(new DERSequence(whole));
549: dout.close();
550:
551: return bOut.toByteArray();
552: } catch (Exception e) {
553: throw new RuntimeException(e.toString());
554: }
555: }
556: }
|