1 /*
2 * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package sun.security.pkcs;
27
28 import java.io;
29 import java.math.BigInteger;
30 import java.util;
31 import java.security.cert.Certificate;
32 import java.security.cert.X509Certificate;
33 import java.security.cert.CertificateException;
34 import java.security.cert.X509CRL;
35 import java.security.cert.CRLException;
36 import java.security.cert.CertificateFactory;
37 import java.security;
38
39 import sun.security.util;
40 import sun.security.x509.AlgorithmId;
41 import sun.security.x509.CertificateIssuerName;
42 import sun.security.x509.X509CertImpl;
43 import sun.security.x509.X509CertInfo;
44 import sun.security.x509.X509CRLImpl;
45 import sun.security.x509.X500Name;
46
47 /**
48 * PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile
49 * Supports only <tt>SignedData</tt> ContentInfo
50 * type, where to the type of data signed is plain Data.
51 * For signedData, <tt>crls</tt>, <tt>attributes</tt> and
52 * PKCS#6 Extended Certificates are not supported.
53 *
54 * @author Benjamin Renaud
55 */
56 public class PKCS7 {
57
58 private ObjectIdentifier contentType;
59
60 // the ASN.1 members for a signedData (and other) contentTypes
61 private BigInteger version = null;
62 private AlgorithmId[] digestAlgorithmIds = null;
63 private ContentInfo contentInfo = null;
64 private X509Certificate[] certificates = null;
65 private X509CRL[] crls = null;
66 private SignerInfo[] signerInfos = null;
67
68 private boolean oldStyle = false; // Is this JDK1.1.x-style?
69
70 private Principal[] certIssuerNames;
71
72 /**
73 * Unmarshals a PKCS7 block from its encoded form, parsing the
74 * encoded bytes from the InputStream.
75 *
76 * @param in an input stream holding at least one PKCS7 block.
77 * @exception ParsingException on parsing errors.
78 * @exception IOException on other errors.
79 */
80 public PKCS7(InputStream in) throws ParsingException, IOException {
81 DataInputStream dis = new DataInputStream(in);
82 byte[] data = new byte[dis.available()];
83 dis.readFully(data);
84
85 parse(new DerInputStream(data));
86 }
87
88 /**
89 * Unmarshals a PKCS7 block from its encoded form, parsing the
90 * encoded bytes from the DerInputStream.
91 *
92 * @param derin a DerInputStream holding at least one PKCS7 block.
93 * @exception ParsingException on parsing errors.
94 */
95 public PKCS7(DerInputStream derin) throws ParsingException {
96 parse(derin);
97 }
98
99 /**
100 * Unmarshals a PKCS7 block from its encoded form, parsing the
101 * encoded bytes.
102 *
103 * @param bytes the encoded bytes.
104 * @exception ParsingException on parsing errors.
105 */
106 public PKCS7(byte[] bytes) throws ParsingException {
107 try {
108 DerInputStream derin = new DerInputStream(bytes);
109 parse(derin);
110 } catch (IOException ioe1) {
111 ParsingException pe = new ParsingException(
112 "Unable to parse the encoded bytes");
113 pe.initCause(ioe1);
114 throw pe;
115 }
116 }
117
118 /*
119 * Parses a PKCS#7 block.
120 */
121 private void parse(DerInputStream derin)
122 throws ParsingException
123 {
124 try {
125 derin.mark(derin.available());
126 // try new (i.e., JDK1.2) style
127 parse(derin, false);
128 } catch (IOException ioe) {
129 try {
130 derin.reset();
131 // try old (i.e., JDK1.1.x) style
132 parse(derin, true);
133 oldStyle = true;
134 } catch (IOException ioe1) {
135 ParsingException pe = new ParsingException(
136 ioe1.getMessage());
137 pe.initCause(ioe1);
138 throw pe;
139 }
140 }
141 }
142
143 /**
144 * Parses a PKCS#7 block.
145 *
146 * @param derin the ASN.1 encoding of the PKCS#7 block.
147 * @param oldStyle flag indicating whether or not the given PKCS#7 block
148 * is encoded according to JDK1.1.x.
149 */
150 private void parse(DerInputStream derin, boolean oldStyle)
151 throws IOException
152 {
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.equals(ContentInfo.NETSCAPE_CERT_SEQUENCE_OID)){
163 parseNetscapeCertChain(content);
164 } else {
165 throw new ParsingException("content type " + contentType +
166 " not supported.");
167 }
168 }
169
170 /**
171 * Construct an initialized PKCS7 block.
172 *
173 * @param digestAlgorithmIds the message digest algorithm identifiers.
174 * @param contentInfo the content information.
175 * @param certificates an array of X.509 certificates.
176 * @param signerInfos an array of signer information.
177 */
178 public PKCS7(AlgorithmId[] digestAlgorithmIds,
179 ContentInfo contentInfo,
180 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] =
212 (X509Certificate)certfac.generateCertificate(bais);
213 bais.close();
214 bais = null;
215 }
216 } catch (CertificateException ce) {
217 ParsingException pe = new ParsingException(ce.getMessage());
218 pe.initCause(ce);
219 throw pe;
220 } catch (IOException ioe) {
221 ParsingException pe = new ParsingException(ioe.getMessage());
222 pe.initCause(ioe);
223 throw pe;
224 } finally {
225 if (bais != null)
226 bais.close();
227 }
228 }
229 }
230
231 private void parseSignedData(DerValue val)
232 throws ParsingException, IOException {
233
234 DerInputStream dis = val.toDerInputStream();
235
236 // Version
237 version = dis.getBigInteger();
238
239 // digestAlgorithmIds
240 DerValue[] digestAlgorithmIdVals = dis.getSet(1);
241 int len = digestAlgorithmIdVals.length;
242 digestAlgorithmIds = new AlgorithmId[len];
243 try {
244 for (int i = 0; i < len; i++) {
245 DerValue oid = digestAlgorithmIdVals[i];
246 digestAlgorithmIds[i] = AlgorithmId.parse(oid);
247 }
248
249 } catch (IOException e) {
250 ParsingException pe =
251 new ParsingException("Error parsing digest AlgorithmId IDs: " +
252 e.getMessage());
253 pe.initCause(e);
254 throw pe;
255 }
256 // contentInfo
257 contentInfo = new ContentInfo(dis);
258
259 CertificateFactory certfac = null;
260 try {
261 certfac = CertificateFactory.getInstance("X.509");
262 } catch (CertificateException ce) {
263 // do nothing
264 }
265
266 /*
267 * check if certificates (implicit tag) are provided
268 * (certificates are OPTIONAL)
269 */
270 if ((byte)(dis.peekByte()) == (byte)0xA0) {
271 DerValue[] certVals = dis.getSet(2, true);
272
273 len = certVals.length;
274 certificates = new X509Certificate[len];
275
276 for (int i = 0; i < len; i++) {
277 ByteArrayInputStream bais = null;
278 try {
279 if (certfac == null)
280 certificates[i] = new X509CertImpl(certVals[i]);
281 else {
282 byte[] encoded = certVals[i].toByteArray();
283 bais = new ByteArrayInputStream(encoded);
284 certificates[i] =
285 (X509Certificate)certfac.generateCertificate(bais);
286 bais.close();
287 bais = null;
288 }
289 } catch (CertificateException ce) {
290 ParsingException pe = new ParsingException(ce.getMessage());
291 pe.initCause(ce);
292 throw pe;
293 } catch (IOException ioe) {
294 ParsingException pe = new ParsingException(ioe.getMessage());
295 pe.initCause(ioe);
296 throw pe;
297 } finally {
298 if (bais != null)
299 bais.close();
300 }
301 }
302 }
303
304 // check if crls (implicit tag) are provided (crls are OPTIONAL)
305 if ((byte)(dis.peekByte()) == (byte)0xA1) {
306 DerValue[] crlVals = dis.getSet(1, true);
307
308 len = crlVals.length;
309 crls = new X509CRL[len];
310
311 for (int i = 0; i < len; i++) {
312 ByteArrayInputStream bais = null;
313 try {
314 if (certfac == null)
315 crls[i] = (X509CRL) new X509CRLImpl(crlVals[i]);
316 else {
317 byte[] encoded = crlVals[i].toByteArray();
318 bais = new ByteArrayInputStream(encoded);
319 crls[i] = (X509CRL) certfac.generateCRL(bais);
320 bais.close();
321 bais = null;
322 }
323 } catch (CRLException e) {
324 ParsingException pe =
325 new ParsingException(e.getMessage());
326 pe.initCause(e);
327 throw pe;
328 } finally {
329 if (bais != null)
330 bais.close();
331 }
332 }
333 }
334
335 // signerInfos
336 DerValue[] signerInfoVals = dis.getSet(1);
337
338 len = signerInfoVals.length;
339 signerInfos = new SignerInfo[len];
340
341 for (int i = 0; i < len; i++) {
342 DerInputStream in = signerInfoVals[i].toDerInputStream();
343 signerInfos[i] = new SignerInfo(in);
344 }
345 }
346
347 /*
348 * Parses an old-style SignedData encoding (for backwards
349 * compatibility with JDK1.1.x).
350 */
351 private void parseOldSignedData(DerValue val)
352 throws ParsingException, IOException
353 {
354 DerInputStream dis = val.toDerInputStream();
355
356 // Version
357 version = dis.getBigInteger();
358
359 // digestAlgorithmIds
360 DerValue[] digestAlgorithmIdVals = dis.getSet(1);
361 int len = digestAlgorithmIdVals.length;
362
363 digestAlgorithmIds = new AlgorithmId[len];
364 try {
365 for (int i = 0; i < len; i++) {
366 DerValue oid = digestAlgorithmIdVals[i];
367 digestAlgorithmIds[i] = AlgorithmId.parse(oid);
368 }
369 } catch (IOException e) {
370 throw new ParsingException("Error parsing digest AlgorithmId IDs");
371 }
372
373 // contentInfo
374 contentInfo = new ContentInfo(dis, true);
375
376 // certificates
377 CertificateFactory certfac = null;
378 try {
379 certfac = CertificateFactory.getInstance("X.509");
380 } catch (CertificateException ce) {
381 // do nothing
382 }
383 DerValue[] certVals = dis.getSet(2);
384 len = certVals.length;
385 certificates = new X509Certificate[len];
386
387 for (int i = 0; i < len; i++) {
388 ByteArrayInputStream bais = null;
389 try {
390 if (certfac == null)
391 certificates[i] = new X509CertImpl(certVals[i]);
392 else {
393 byte[] encoded = certVals[i].toByteArray();
394 bais = new ByteArrayInputStream(encoded);
395 certificates[i] =
396 (X509Certificate)certfac.generateCertificate(bais);
397 bais.close();
398 bais = null;
399 }
400 } catch (CertificateException ce) {
401 ParsingException pe = new ParsingException(ce.getMessage());
402 pe.initCause(ce);
403 throw pe;
404 } catch (IOException ioe) {
405 ParsingException pe = new ParsingException(ioe.getMessage());
406 pe.initCause(ioe);
407 throw pe;
408 } finally {
409 if (bais != null)
410 bais.close();
411 }
412 }
413
414 // crls are ignored.
415 dis.getSet(0);
416
417 // signerInfos
418 DerValue[] signerInfoVals = dis.getSet(1);
419 len = signerInfoVals.length;
420 signerInfos = new SignerInfo[len];
421 for (int i = 0; i < len; i++) {
422 DerInputStream in = signerInfoVals[i].toDerInputStream();
423 signerInfos[i] = new SignerInfo(in, true);
424 }
425 }
426
427 /**
428 * Encodes the signed data to an output stream.
429 *
430 * @param out the output stream to write the encoded data to.
431 * @exception IOException on encoding errors.
432 */
433 public void encodeSignedData(OutputStream out) throws IOException {
434 DerOutputStream derout = new DerOutputStream();
435 encodeSignedData(derout);
436 out.write(derout.toByteArray());
437 }
438
439 /**
440 * Encodes the signed data to a DerOutputStream.
441 *
442 * @param out the DerOutputStream to write the encoded data to.
443 * @exception IOException on encoding errors.
444 */
445 public void encodeSignedData(DerOutputStream out)
446 throws IOException
447 {
448 DerOutputStream signedData = new DerOutputStream();
449
450 // version
451 signedData.putInteger(version);
452
453 // digestAlgorithmIds
454 signedData.putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds);
455
456 // contentInfo
457 contentInfo.encode(signedData);
458
459 // certificates (optional)
460 if (certificates != null && certificates.length != 0) {
461 // cast to X509CertImpl[] since X509CertImpl implements DerEncoder
462 X509CertImpl implCerts[] = new X509CertImpl[certificates.length];
463 for (int i = 0; i < certificates.length; i++) {
464 if (certificates[i] instanceof X509CertImpl)
465 implCerts[i] = (X509CertImpl) certificates[i];
466 else {
467 try {
468 byte[] encoded = certificates[i].getEncoded();
469 implCerts[i] = new X509CertImpl(encoded);
470 } catch (CertificateException ce) {
471 IOException ie = new IOException(ce.getMessage());
472 ie.initCause(ce);
473 throw ie;
474 }
475 }
476 }
477
478 // Add the certificate set (tagged with [0] IMPLICIT)
479 // to the signed data
480 signedData.putOrderedSetOf((byte)0xA0, implCerts);
481 }
482
483 // no crls (OPTIONAL field)
484
485 // signerInfos
486 signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos);
487
488 // making it a signed data block
489 DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence,
490 signedData.toByteArray());
491
492 // making it a content info sequence
493 ContentInfo block = new ContentInfo(ContentInfo.SIGNED_DATA_OID,
494 signedDataSeq);
495
496 // writing out the contentInfo sequence
497 block.encode(out);
498 }
499
500 /**
501 * This verifies a given SignerInfo.
502 *
503 * @param info the signer information.
504 * @param bytes the DER encoded content information.
505 *
506 * @exception NoSuchAlgorithmException on unrecognized algorithms.
507 * @exception SignatureException on signature handling errors.
508 */
509 public SignerInfo verify(SignerInfo info, byte[] bytes)
510 throws NoSuchAlgorithmException, SignatureException {
511 return info.verify(this, bytes);
512 }
513
514 /**
515 * Returns all signerInfos which self-verify.
516 *
517 * @param bytes the DER encoded content information.
518 *
519 * @exception NoSuchAlgorithmException on unrecognized algorithms.
520 * @exception SignatureException on signature handling errors.
521 */
522 public SignerInfo[] verify(byte[] bytes)
523 throws NoSuchAlgorithmException, SignatureException {
524
525 Vector<SignerInfo> intResult = new Vector<SignerInfo>();
526 for (int i = 0; i < signerInfos.length; i++) {
527
528 SignerInfo signerInfo = verify(signerInfos[i], bytes);
529 if (signerInfo != null) {
530 intResult.addElement(signerInfo);
531 }
532 }
533 if (intResult.size() != 0) {
534
535 SignerInfo[] result = new SignerInfo[intResult.size()];
536 intResult.copyInto(result);
537 return result;
538 }
539 return null;
540 }
541
542 /**
543 * Returns all signerInfos which self-verify.
544 *
545 * @exception NoSuchAlgorithmException on unrecognized algorithms.
546 * @exception SignatureException on signature handling errors.
547 */
548 public SignerInfo[] verify()
549 throws NoSuchAlgorithmException, SignatureException {
550 return verify(null);
551 }
552
553 /**
554 * Returns the version number of this PKCS7 block.
555 * @return the version or null if version is not specified
556 * for the content type.
557 */
558 public BigInteger getVersion() {
559 return version;
560 }
561
562 /**
563 * Returns the message digest algorithms specified in this PKCS7 block.
564 * @return the array of Digest Algorithms or null if none are specified
565 * for the content type.
566 */
567 public AlgorithmId[] getDigestAlgorithmIds() {
568 return digestAlgorithmIds;
569 }
570
571 /**
572 * Returns the content information specified in this PKCS7 block.
573 */
574 public ContentInfo getContentInfo() {
575 return contentInfo;
576 }
577
578 /**
579 * Returns the X.509 certificates listed in this PKCS7 block.
580 * @return a clone of the array of X.509 certificates or null if
581 * none are specified for the content type.
582 */
583 public X509Certificate[] getCertificates() {
584 if (certificates != null)
585 return certificates.clone();
586 else
587 return null;
588 }
589
590 /**
591 * Returns the X.509 crls listed in this PKCS7 block.
592 * @return a clone of the array of X.509 crls or null if none
593 * are specified for the content type.
594 */
595 public X509CRL[] getCRLs() {
596 if (crls != null)
597 return crls.clone();
598 else
599 return null;
600 }
601
602 /**
603 * Returns the signer's information specified in this PKCS7 block.
604 * @return the array of Signer Infos or null if none are specified
605 * for the content type.
606 */
607 public SignerInfo[] getSignerInfos() {
608 return signerInfos;
609 }
610
611 /**
612 * Returns the X.509 certificate listed in this PKCS7 block
613 * which has a matching serial number and Issuer name, or
614 * null if one is not found.
615 *
616 * @param serial the serial number of the certificate to retrieve.
617 * @param issuerName the Distinguished Name of the Issuer.
618 */
619 public X509Certificate getCertificate(BigInteger serial, X500Name issuerName) {
620 if (certificates != null) {
621 if (certIssuerNames == null)
622 populateCertIssuerNames();
623 for (int i = 0; i < certificates.length; i++) {
624 X509Certificate cert = certificates[i];
625 BigInteger thisSerial = cert.getSerialNumber();
626 if (serial.equals(thisSerial)
627 && issuerName.equals(certIssuerNames[i]))
628 {
629 return cert;
630 }
631 }
632 }
633 return null;
634 }
635
636 /**
637 * Populate array of Issuer DNs from certificates and convert
638 * each Principal to type X500Name if necessary.
639 */
640 private void populateCertIssuerNames() {
641 if (certificates == null)
642 return;
643
644 certIssuerNames = new Principal[certificates.length];
645 for (int i = 0; i < certificates.length; i++) {
646 X509Certificate cert = certificates[i];
647 Principal certIssuerName = cert.getIssuerDN();
648 if (!(certIssuerName instanceof X500Name)) {
649 // must extract the original encoded form of DN for
650 // subsequent name comparison checks (converting to a
651 // String and back to an encoded DN could cause the
652 // types of String attribute values to be changed)
653 try {
654 X509CertInfo tbsCert =
655 new X509CertInfo(cert.getTBSCertificate());
656 certIssuerName = (Principal)
657 tbsCert.get(CertificateIssuerName.NAME + "." +
658 CertificateIssuerName.DN_NAME);
659 } catch (Exception e) {
660 // error generating X500Name object from the cert's
661 // issuer DN, leave name as is.
662 }
663 }
664 certIssuerNames[i] = certIssuerName;
665 }
666 }
667
668 /**
669 * Returns the PKCS7 block in a printable string form.
670 */
671 public String toString() {
672 String out = "";
673
674 out += contentInfo + "\n";
675 if (version != null)
676 out += "PKCS7 :: version: " + Debug.toHexString(version) + "\n";
677 if (digestAlgorithmIds != null) {
678 out += "PKCS7 :: digest AlgorithmIds: \n";
679 for (int i = 0; i < digestAlgorithmIds.length; i++)
680 out += "\t" + digestAlgorithmIds[i] + "\n";
681 }
682 if (certificates != null) {
683 out += "PKCS7 :: certificates: \n";
684 for (int i = 0; i < certificates.length; i++)
685 out += "\t" + i + ". " + certificates[i] + "\n";
686 }
687 if (crls != null) {
688 out += "PKCS7 :: crls: \n";
689 for (int i = 0; i < crls.length; i++)
690 out += "\t" + i + ". " + crls[i] + "\n";
691 }
692 if (signerInfos != null) {
693 out += "PKCS7 :: signer infos: \n";
694 for (int i = 0; i < signerInfos.length; i++)
695 out += ("\t" + i + ". " + signerInfos[i] + "\n");
696 }
697 return out;
698 }
699
700 /**
701 * Returns true if this is a JDK1.1.x-style PKCS#7 block, and false
702 * otherwise.
703 */
704 public boolean isOldStyle() {
705 return this.oldStyle;
706 }
707 }