001: /*
002: * @(#)PKCS7.java 1.52 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.security.pkcs;
029:
030: import java.io.*;
031: import java.math.BigInteger;
032: import java.util.*;
033: import java.security.cert.Certificate;
034: import java.security.cert.X509Certificate;
035: import java.security.cert.CertificateException;
036: import java.security.cert.X509CRL;
037: import java.security.cert.CRLException;
038: import java.security.cert.CertificateFactory;
039: import java.security.*;
040:
041: import sun.security.util.*;
042: import sun.security.x509.AlgorithmId;
043: import sun.security.x509.CertificateIssuerName;
044: import sun.security.x509.X509CertImpl;
045: import sun.security.x509.X509CertInfo;
046: import sun.security.x509.X509CRLImpl;
047: import sun.security.x509.X500Name;
048:
049: /**
050: * PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile
051: * Supports only <tt>SignedData</tt> ContentInfo
052: * type, where to the type of data signed is plain Data.
053: * For signedData, <tt>crls</tt>, <tt>attributes</tt> and
054: * PKCS#6 Extended Certificates are not supported.
055: *
056: * @version 1.45 02/02/00
057: * @author Benjamin Renaud
058: */
059: public class PKCS7 {
060:
061: private ObjectIdentifier contentType;
062:
063: // the ASN.1 members for a signedData (and other) contentTypes
064: private BigInteger version = null;
065: private AlgorithmId[] digestAlgorithmIds = null;
066: private ContentInfo contentInfo = null;
067: private X509Certificate[] certificates = null;
068: private X509CRL[] crls = null;
069: private SignerInfo[] signerInfos = null;
070:
071: private boolean oldStyle = false; // Is this JDK1.1.x-style?
072:
073: private Principal[] certIssuerNames;
074:
075: /**
076: * Unmarshals a PKCS7 block from its encoded form, parsing the
077: * encoded bytes from the InputStream.
078: *
079: * @param in an input stream holding at least one PKCS7 block.
080: * @exception ParsingException on parsing errors.
081: * @exception IOException on other errors.
082: */
083: public PKCS7(InputStream in) throws ParsingException, IOException {
084: DataInputStream dis = new DataInputStream(in);
085: byte[] data = new byte[dis.available()];
086: dis.readFully(data);
087:
088: parse(new DerInputStream(data));
089: }
090:
091: /**
092: * Unmarshals a PKCS7 block from its encoded form, parsing the
093: * encoded bytes from the DerInputStream.
094: *
095: * @param derin a DerInputStream holding at least one PKCS7 block.
096: * @exception ParsingException on parsing errors.
097: */
098: public PKCS7(DerInputStream derin) throws ParsingException {
099: parse(derin);
100: }
101:
102: /**
103: * Unmarshals a PKCS7 block from its encoded form, parsing the
104: * encoded bytes.
105: *
106: * @param bytes the encoded bytes.
107: * @exception ParsingException on parsing errors.
108: */
109: public PKCS7(byte[] bytes) throws ParsingException {
110: try {
111: DerInputStream derin = new DerInputStream(bytes);
112: parse(derin);
113: } catch (IOException ioe1) {
114: ParsingException pe = new ParsingException(
115: "Unable to parse the encoded bytes");
116: pe.initCause(ioe1);
117: throw pe;
118: }
119: }
120:
121: /*
122: * Parses a PKCS#7 block.
123: */
124: private void parse(DerInputStream derin) throws ParsingException {
125: try {
126: derin.mark(derin.available());
127: // try new (i.e., JDK1.2) style
128: parse(derin, false);
129: } catch (IOException ioe) {
130: try {
131: derin.reset();
132: // try old (i.e., JDK1.1.x) style
133: parse(derin, true);
134: oldStyle = true;
135: } catch (IOException ioe1) {
136: ParsingException pe = new ParsingException(ioe1
137: .getMessage());
138: pe.initCause(ioe1);
139: throw pe;
140: }
141: }
142: }
143:
144: /**
145: * Parses a PKCS#7 block.
146: *
147: * @param derin the ASN.1 encoding of the PKCS#7 block.
148: * @param oldStyle flag indicating whether or not the given PKCS#7 block
149: * is encoded according to JDK1.1.x.
150: */
151: private void parse(DerInputStream derin, boolean oldStyle)
152: throws IOException {
153: contentInfo = new ContentInfo(derin, oldStyle);
154: contentType = contentInfo.contentType;
155: DerValue content = contentInfo.getContent();
156:
157: if (contentType.equals(ContentInfo.SIGNED_DATA_OID)) {
158: parseSignedData(content);
159: } else if (contentType.equals(ContentInfo.OLD_SIGNED_DATA_OID)) {
160: // This is for backwards compatibility with JDK 1.1.x
161: parseOldSignedData(content);
162: } else if (contentType
163: .equals(ContentInfo.NETSCAPE_CERT_SEQUENCE_OID)) {
164: parseNetscapeCertChain(content);
165: } else {
166: throw new ParsingException("content type " + contentType
167: + " not supported.");
168: }
169: }
170:
171: /**
172: * Construct an initialized PKCS7 block.
173: *
174: * @param digestAlgorithmIds the message digest algorithm identifiers.
175: * @param contentInfo the content information.
176: * @param certificates an array of X.509 certificates.
177: * @param signerInfos an array of signer information.
178: */
179: public PKCS7(AlgorithmId[] digestAlgorithmIds,
180: ContentInfo contentInfo, X509Certificate[] certificates,
181: SignerInfo[] signerInfos) {
182:
183: version = BigInteger.ONE;
184: this .digestAlgorithmIds = digestAlgorithmIds;
185: this .contentInfo = contentInfo;
186: this .certificates = certificates;
187: this .signerInfos = signerInfos;
188: }
189:
190: private void parseNetscapeCertChain(DerValue val)
191: throws ParsingException, IOException {
192: DerInputStream dis = new DerInputStream(val.toByteArray());
193: DerValue[] contents = dis.getSequence(2);
194: certificates = new X509Certificate[contents.length];
195:
196: CertificateFactory certfac = null;
197: try {
198: certfac = CertificateFactory.getInstance("X.509");
199: } catch (CertificateException ce) {
200: // do nothing
201: }
202:
203: for (int i = 0; i < contents.length; i++) {
204: ByteArrayInputStream bais = null;
205: try {
206: if (certfac == null)
207: certificates[i] = new X509CertImpl(contents[i]);
208: else {
209: byte[] encoded = contents[i].toByteArray();
210: bais = new ByteArrayInputStream(encoded);
211: certificates[i] = (X509Certificate) certfac
212: .generateCertificate(bais);
213: bais.close();
214: bais = null;
215: }
216: } catch (CertificateException ce) {
217: ParsingException pe = new ParsingException(ce
218: .getMessage());
219: pe.initCause(ce);
220: throw pe;
221: } catch (IOException ioe) {
222: ParsingException pe = new ParsingException(ioe
223: .getMessage());
224: pe.initCause(ioe);
225: throw pe;
226: } finally {
227: if (bais != null)
228: bais.close();
229: }
230: }
231: }
232:
233: private void parseSignedData(DerValue val) throws ParsingException,
234: IOException {
235:
236: DerInputStream dis = val.toDerInputStream();
237:
238: // Version
239: version = dis.getBigInteger();
240:
241: // digestAlgorithmIds
242: DerValue[] digestAlgorithmIdVals = dis.getSet(1);
243: int len = digestAlgorithmIdVals.length;
244: digestAlgorithmIds = new AlgorithmId[len];
245: try {
246: for (int i = 0; i < len; i++) {
247: DerValue oid = digestAlgorithmIdVals[i];
248: digestAlgorithmIds[i] = AlgorithmId.parse(oid);
249: }
250:
251: } catch (IOException e) {
252: ParsingException pe = new ParsingException(
253: "Error parsing digest AlgorithmId IDs: "
254: + e.getMessage());
255: pe.initCause(e);
256: throw pe;
257: }
258: // contentInfo
259: contentInfo = new ContentInfo(dis);
260:
261: CertificateFactory certfac = null;
262: try {
263: certfac = CertificateFactory.getInstance("X.509");
264: } catch (CertificateException ce) {
265: // do nothing
266: }
267:
268: /*
269: * check if certificates (implicit tag) are provided
270: * (certificates are OPTIONAL)
271: */
272: if ((byte) (dis.peekByte()) == (byte) 0xA0) {
273: DerValue[] certVals = dis.getSet(2, true);
274:
275: len = certVals.length;
276: certificates = new X509Certificate[len];
277:
278: for (int i = 0; i < len; i++) {
279: ByteArrayInputStream bais = null;
280: try {
281: if (certfac == null)
282: certificates[i] = new X509CertImpl(certVals[i]);
283: else {
284: byte[] encoded = certVals[i].toByteArray();
285: bais = new ByteArrayInputStream(encoded);
286: certificates[i] = (X509Certificate) certfac
287: .generateCertificate(bais);
288: bais.close();
289: bais = null;
290: }
291: } catch (CertificateException ce) {
292: ParsingException pe = new ParsingException(ce
293: .getMessage());
294: pe.initCause(ce);
295: throw pe;
296: } catch (IOException ioe) {
297: ParsingException pe = new ParsingException(ioe
298: .getMessage());
299: pe.initCause(ioe);
300: throw pe;
301: } finally {
302: if (bais != null)
303: bais.close();
304: }
305: }
306: }
307:
308: // check if crls (implicit tag) are provided (crls are OPTIONAL)
309: if ((byte) (dis.peekByte()) == (byte) 0xA1) {
310: DerValue[] crlVals = dis.getSet(1, true);
311:
312: len = crlVals.length;
313: crls = new X509CRL[len];
314:
315: for (int i = 0; i < len; i++) {
316: ByteArrayInputStream bais = null;
317: try {
318: if (certfac == null)
319: crls[i] = (X509CRL) new X509CRLImpl(crlVals[i]);
320: else {
321: byte[] encoded = crlVals[i].toByteArray();
322: bais = new ByteArrayInputStream(encoded);
323: crls[i] = (X509CRL) certfac.generateCRL(bais);
324: bais.close();
325: bais = null;
326: }
327: } catch (CRLException e) {
328: ParsingException pe = new ParsingException(e
329: .getMessage());
330: pe.initCause(e);
331: throw pe;
332: } finally {
333: if (bais != null)
334: bais.close();
335: }
336: }
337: }
338:
339: // signerInfos
340: DerValue[] signerInfoVals = dis.getSet(1);
341:
342: len = signerInfoVals.length;
343: signerInfos = new SignerInfo[len];
344:
345: for (int i = 0; i < len; i++) {
346: DerInputStream in = signerInfoVals[i].toDerInputStream();
347: signerInfos[i] = new SignerInfo(in);
348: }
349: }
350:
351: /*
352: * Parses an old-style SignedData encoding (for backwards
353: * compatibility with JDK1.1.x).
354: */
355: private void parseOldSignedData(DerValue val)
356: throws ParsingException, IOException {
357: DerInputStream dis = val.toDerInputStream();
358:
359: // Version
360: version = dis.getBigInteger();
361:
362: // digestAlgorithmIds
363: DerValue[] digestAlgorithmIdVals = dis.getSet(1);
364: int len = digestAlgorithmIdVals.length;
365:
366: digestAlgorithmIds = new AlgorithmId[len];
367: try {
368: for (int i = 0; i < len; i++) {
369: DerValue oid = digestAlgorithmIdVals[i];
370: digestAlgorithmIds[i] = AlgorithmId.parse(oid);
371: }
372: } catch (IOException e) {
373: throw new ParsingException(
374: "Error parsing digest AlgorithmId IDs");
375: }
376:
377: // contentInfo
378: contentInfo = new ContentInfo(dis, true);
379:
380: // certificates
381: CertificateFactory certfac = null;
382: try {
383: certfac = CertificateFactory.getInstance("X.509");
384: } catch (CertificateException ce) {
385: // do nothing
386: }
387: DerValue[] certVals = dis.getSet(2);
388: len = certVals.length;
389: certificates = new X509Certificate[len];
390:
391: for (int i = 0; i < len; i++) {
392: ByteArrayInputStream bais = null;
393: try {
394: if (certfac == null)
395: certificates[i] = new X509CertImpl(certVals[i]);
396: else {
397: byte[] encoded = certVals[i].toByteArray();
398: bais = new ByteArrayInputStream(encoded);
399: certificates[i] = (X509Certificate) certfac
400: .generateCertificate(bais);
401: bais.close();
402: bais = null;
403: }
404: } catch (CertificateException ce) {
405: ParsingException pe = new ParsingException(ce
406: .getMessage());
407: pe.initCause(ce);
408: throw pe;
409: } catch (IOException ioe) {
410: ParsingException pe = new ParsingException(ioe
411: .getMessage());
412: pe.initCause(ioe);
413: throw pe;
414: } finally {
415: if (bais != null)
416: bais.close();
417: }
418: }
419:
420: // crls are ignored.
421: dis.getSet(0);
422:
423: // signerInfos
424: DerValue[] signerInfoVals = dis.getSet(1);
425: len = signerInfoVals.length;
426: signerInfos = new SignerInfo[len];
427: for (int i = 0; i < len; i++) {
428: DerInputStream in = signerInfoVals[i].toDerInputStream();
429: signerInfos[i] = new SignerInfo(in, true);
430: }
431: }
432:
433: /**
434: * Encodes the signed data to an output stream.
435: *
436: * @param out the output stream to write the encoded data to.
437: * @exception IOException on encoding errors.
438: */
439: public void encodeSignedData(OutputStream out) throws IOException {
440: DerOutputStream derout = new DerOutputStream();
441: encodeSignedData(derout);
442: out.write(derout.toByteArray());
443: }
444:
445: /**
446: * Encodes the signed data to a DerOutputStream.
447: *
448: * @param out the DerOutputStream to write the encoded data to.
449: * @exception IOException on encoding errors.
450: */
451: public void encodeSignedData(DerOutputStream out)
452: throws IOException {
453: DerOutputStream signedData = new DerOutputStream();
454:
455: // version
456: signedData.putInteger(version);
457:
458: // digestAlgorithmIds
459: signedData
460: .putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds);
461:
462: // contentInfo
463: contentInfo.encode(signedData);
464:
465: // certificates (optional)
466: if (certificates != null && certificates.length != 0) {
467: // cast to X509CertImpl[] since X509CertImpl implements DerEncoder
468: X509CertImpl implCerts[] = new X509CertImpl[certificates.length];
469: for (int i = 0; i < certificates.length; i++) {
470: if (certificates[i] instanceof X509CertImpl)
471: implCerts[i] = (X509CertImpl) certificates[i];
472: else {
473: try {
474: byte[] encoded = certificates[i].getEncoded();
475: implCerts[i] = new X509CertImpl(encoded);
476: } catch (CertificateException ce) {
477: IOException ie = new IOException(ce
478: .getMessage());
479: ie.initCause(ce);
480: throw ie;
481: }
482: }
483: }
484:
485: // Add the certificate set (tagged with [0] IMPLICIT)
486: // to the signed data
487: signedData.putOrderedSetOf((byte) 0xA0, implCerts);
488: }
489:
490: // no crls (OPTIONAL field)
491:
492: // signerInfos
493: signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos);
494:
495: // making it a signed data block
496: DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence,
497: signedData.toByteArray());
498:
499: // making it a content info sequence
500: ContentInfo block = new ContentInfo(
501: ContentInfo.SIGNED_DATA_OID, signedDataSeq);
502:
503: // writing out the contentInfo sequence
504: block.encode(out);
505: }
506:
507: /**
508: * This verifies a given SignerInfo.
509: *
510: * @param info the signer information.
511: * @param bytes the DER encoded content information.
512: *
513: * @exception NoSuchAlgorithmException on unrecognized algorithms.
514: * @exception SignatureException on signature handling errors.
515: */
516: public SignerInfo verify(SignerInfo info, byte[] bytes)
517: throws NoSuchAlgorithmException, SignatureException {
518: return info.verify(this , bytes);
519: }
520:
521: /**
522: * Returns all signerInfos which self-verify.
523: *
524: * @param bytes the DER encoded content information.
525: *
526: * @exception NoSuchAlgorithmException on unrecognized algorithms.
527: * @exception SignatureException on signature handling errors.
528: */
529: public SignerInfo[] verify(byte[] bytes)
530: throws NoSuchAlgorithmException, SignatureException {
531:
532: Vector intResult = new Vector();
533: for (int i = 0; i < signerInfos.length; i++) {
534:
535: SignerInfo signerInfo = verify(signerInfos[i], bytes);
536: if (signerInfo != null) {
537: intResult.addElement(signerInfo);
538: }
539: }
540: if (intResult.size() != 0) {
541:
542: SignerInfo[] result = new SignerInfo[intResult.size()];
543: intResult.copyInto(result);
544: return result;
545: }
546: return null;
547: }
548:
549: /**
550: * Returns all signerInfos which self-verify.
551: *
552: * @exception NoSuchAlgorithmException on unrecognized algorithms.
553: * @exception SignatureException on signature handling errors.
554: */
555: public SignerInfo[] verify() throws NoSuchAlgorithmException,
556: SignatureException {
557: return verify(null);
558: }
559:
560: /**
561: * Returns the version number of this PKCS7 block.
562: * @return the version or null if version is not specified
563: * for the content type.
564: */
565: public BigInteger getVersion() {
566: return version;
567: }
568:
569: /**
570: * Returns the message digest algorithms specified in this PKCS7 block.
571: * @return the array of Digest Algorithms or null if none are specified
572: * for the content type.
573: */
574: public AlgorithmId[] getDigestAlgorithmIds() {
575: return digestAlgorithmIds;
576: }
577:
578: /**
579: * Returns the content information specified in this PKCS7 block.
580: */
581: public ContentInfo getContentInfo() {
582: return contentInfo;
583: }
584:
585: /**
586: * Returns the X.509 certificates listed in this PKCS7 block.
587: * @return a clone of the array of X.509 certificates or null if
588: * none are specified for the content type.
589: */
590: public X509Certificate[] getCertificates() {
591: if (certificates != null)
592: return (X509Certificate[]) certificates.clone();
593: else
594: return null;
595: }
596:
597: /**
598: * Returns the X.509 crls listed in this PKCS7 block.
599: * @return a clone of the array of X.509 crls or null if none
600: * are specified for the content type.
601: */
602: public X509CRL[] getCRLs() {
603: if (crls != null)
604: return (X509CRL[]) crls.clone();
605: else
606: return null;
607: }
608:
609: /**
610: * Returns the signer's information specified in this PKCS7 block.
611: * @return the array of Signer Infos or null if none are specified
612: * for the content type.
613: */
614: public SignerInfo[] getSignerInfos() {
615: return signerInfos;
616: }
617:
618: /**
619: * Returns the X.509 certificate listed in this PKCS7 block
620: * which has a matching serial number and Issuer name, or
621: * null if one is not found.
622: *
623: * @param serial the serial number of the certificate to retrieve.
624: * @param issuerName the Distinguished Name of the Issuer.
625: */
626: public X509Certificate getCertificate(BigInteger serial,
627: X500Name issuerName) {
628: if (certificates != null) {
629: if (certIssuerNames == null)
630: populateCertIssuerNames();
631: for (int i = 0; i < certificates.length; i++) {
632: X509Certificate cert = certificates[i];
633: BigInteger this Serial = cert.getSerialNumber();
634: if (serial.equals(this Serial)
635: && issuerName.equals(certIssuerNames[i])) {
636: return cert;
637: }
638: }
639: }
640: return null;
641: }
642:
643: /**
644: * Populate array of Issuer DNs from certificates and convert
645: * each Principal to type X500Name if necessary.
646: */
647: private void populateCertIssuerNames() {
648: if (certificates == null)
649: return;
650:
651: certIssuerNames = new Principal[certificates.length];
652: for (int i = 0; i < certificates.length; i++) {
653: X509Certificate cert = certificates[i];
654: Principal certIssuerName = cert.getIssuerDN();
655: if (!(certIssuerName instanceof X500Name)) {
656: // must extract the original encoded form of DN for
657: // subsequent name comparison checks (converting to a
658: // String and back to an encoded DN could cause the
659: // types of String attribute values to be changed)
660: try {
661: X509CertInfo tbsCert = new X509CertInfo(cert
662: .getTBSCertificate());
663: certIssuerName = (Principal) tbsCert
664: .get(CertificateIssuerName.NAME + "."
665: + CertificateIssuerName.DN_NAME);
666: } catch (Exception e) {
667: // error generating X500Name object from the cert's
668: // issuer DN, leave name as is.
669: }
670: }
671: certIssuerNames[i] = certIssuerName;
672: }
673: }
674:
675: /**
676: * Returns the PKCS7 block in a printable string form.
677: */
678: public String toString() {
679: String out = "";
680:
681: out += contentInfo + "\n";
682: if (version != null)
683: out += "PKCS7 :: version: " + Debug.toHexString(version)
684: + "\n";
685: if (digestAlgorithmIds != null) {
686: out += "PKCS7 :: digest AlgorithmIds: \n";
687: for (int i = 0; i < digestAlgorithmIds.length; i++)
688: out += "\t" + digestAlgorithmIds[i] + "\n";
689: }
690: if (certificates != null) {
691: out += "PKCS7 :: certificates: \n";
692: for (int i = 0; i < certificates.length; i++)
693: out += "\t" + i + ". " + certificates[i] + "\n";
694: }
695: if (crls != null) {
696: out += "PKCS7 :: crls: \n";
697: for (int i = 0; i < crls.length; i++)
698: out += "\t" + i + ". " + crls[i] + "\n";
699: }
700: if (signerInfos != null) {
701: out += "PKCS7 :: signer infos: \n";
702: for (int i = 0; i < signerInfos.length; i++)
703: out += ("\t" + i + ". " + signerInfos[i] + "\n");
704: }
705: return out;
706: }
707:
708: /**
709: * Returns true if this is a JDK1.1.x-style PKCS#7 block, and false
710: * otherwise.
711: */
712: public boolean isOldStyle() {
713: return this.oldStyle;
714: }
715: }
|