1 /*
2  * Copyright (c) 1996, 2020, Oracle and/or its affiliates. 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.pkcs;
27 
28 import java.io.*;
29 import java.math.BigInteger;
30 import java.net.URI;
31 import java.util.*;
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.timestamp.*;
40 import sun.security.util.*;
41 import sun.security.x509.AlgorithmId;
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 {@code SignedData} ContentInfo
50  * type, where to the type of data signed is plain Data.
51  * For signedData, {@code crls}, {@code attributes} 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      * Random number generator for creating nonce values
74      * (Lazy initialization)
75      */
76     private static class SecureRandomHolder {
77         static final SecureRandom RANDOM;
78         static {
79             SecureRandom tmp = null;
80             try {
81                 tmp = SecureRandom.getInstance("SHA1PRNG");
82             } catch (NoSuchAlgorithmException e) {
83                 // should not happen
84             }
85             RANDOM = tmp;
86         }
87     }
88 
89     /*
90      * Object identifier for the timestamping key purpose.
91      */
92     private static final String KP_TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8";
93 
94     /*
95      * Object identifier for extendedKeyUsage extension
96      */
97     private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37";
98 
99     /**
100      * Unmarshals a PKCS7 block from its encoded form, parsing the
101      * encoded bytes from the InputStream.
102      *
103      * @param in an input stream holding at least one PKCS7 block.
104      * @exception ParsingException on parsing errors.
105      * @exception IOException on other errors.
106      */
PKCS7(InputStream in)107     public PKCS7(InputStream in) throws ParsingException, IOException {
108         DataInputStream dis = new DataInputStream(in);
109         byte[] data = new byte[dis.available()];
110         dis.readFully(data);
111 
112         parse(new DerInputStream(data));
113     }
114 
115     /**
116      * Unmarshals a PKCS7 block from its encoded form, parsing the
117      * encoded bytes from the DerInputStream.
118      *
119      * @param derin a DerInputStream holding at least one PKCS7 block.
120      * @exception ParsingException on parsing errors.
121      */
PKCS7(DerInputStream derin)122     public PKCS7(DerInputStream derin) throws ParsingException {
123         parse(derin);
124     }
125 
126     /**
127      * Unmarshals a PKCS7 block from its encoded form, parsing the
128      * encoded bytes.
129      *
130      * @param bytes the encoded bytes.
131      * @exception ParsingException on parsing errors.
132      */
PKCS7(byte[] bytes)133     public PKCS7(byte[] bytes) throws ParsingException {
134         try {
135             DerInputStream derin = new DerInputStream(bytes);
136             parse(derin);
137         } catch (IOException ioe1) {
138             ParsingException pe = new ParsingException(
139                 "Unable to parse the encoded bytes");
140             pe.initCause(ioe1);
141             throw pe;
142         }
143     }
144 
145     /*
146      * Parses a PKCS#7 block.
147      */
parse(DerInputStream derin)148     private void parse(DerInputStream derin)
149         throws ParsingException
150     {
151         try {
152             derin.mark(derin.available());
153             // try new (i.e., JDK1.2) style
154             parse(derin, false);
155         } catch (IOException ioe) {
156             try {
157                 derin.reset();
158                 // try old (i.e., JDK1.1.x) style
159                 parse(derin, true);
160                 oldStyle = true;
161             } catch (IOException ioe1) {
162                 ParsingException pe = new ParsingException(
163                     ioe1.getMessage());
164                 pe.initCause(ioe);
165                 pe.addSuppressed(ioe1);
166                 throw pe;
167             }
168         }
169     }
170 
171     /**
172      * Parses a PKCS#7 block.
173      *
174      * @param derin the ASN.1 encoding of the PKCS#7 block.
175      * @param oldStyle flag indicating whether or not the given PKCS#7 block
176      * is encoded according to JDK1.1.x.
177      */
parse(DerInputStream derin, boolean oldStyle)178     private void parse(DerInputStream derin, boolean oldStyle)
179         throws IOException
180     {
181         contentInfo = new ContentInfo(derin, oldStyle);
182         contentType = contentInfo.contentType;
183         DerValue content = contentInfo.getContent();
184 
185         if (contentType.equals(ContentInfo.SIGNED_DATA_OID)) {
186             parseSignedData(content);
187         } else if (contentType.equals(ContentInfo.OLD_SIGNED_DATA_OID)) {
188             // This is for backwards compatibility with JDK 1.1.x
189             parseOldSignedData(content);
190         } else if (contentType.equals(ContentInfo.NETSCAPE_CERT_SEQUENCE_OID)){
191             parseNetscapeCertChain(content);
192         } else {
193             throw new ParsingException("content type " + contentType +
194                                        " not supported.");
195         }
196     }
197 
198     /**
199      * Construct an initialized PKCS7 block.
200      *
201      * @param digestAlgorithmIds the message digest algorithm identifiers.
202      * @param contentInfo the content information.
203      * @param certificates an array of X.509 certificates.
204      * @param crls an array of CRLs
205      * @param signerInfos an array of signer information.
206      */
PKCS7(AlgorithmId[] digestAlgorithmIds, ContentInfo contentInfo, X509Certificate[] certificates, X509CRL[] crls, SignerInfo[] signerInfos)207     public PKCS7(AlgorithmId[] digestAlgorithmIds,
208                  ContentInfo contentInfo,
209                  X509Certificate[] certificates,
210                  X509CRL[] crls,
211                  SignerInfo[] signerInfos) {
212 
213         version = BigInteger.ONE;
214         this.digestAlgorithmIds = digestAlgorithmIds;
215         this.contentInfo = contentInfo;
216         this.certificates = certificates;
217         this.crls = crls;
218         this.signerInfos = signerInfos;
219     }
220 
PKCS7(AlgorithmId[] digestAlgorithmIds, ContentInfo contentInfo, X509Certificate[] certificates, SignerInfo[] signerInfos)221     public PKCS7(AlgorithmId[] digestAlgorithmIds,
222                  ContentInfo contentInfo,
223                  X509Certificate[] certificates,
224                  SignerInfo[] signerInfos) {
225         this(digestAlgorithmIds, contentInfo, certificates, null, signerInfos);
226     }
227 
parseNetscapeCertChain(DerValue val)228     private void parseNetscapeCertChain(DerValue val)
229     throws ParsingException, IOException {
230         DerInputStream dis = new DerInputStream(val.toByteArray());
231         DerValue[] contents = dis.getSequence(2);
232         certificates = new X509Certificate[contents.length];
233 
234         CertificateFactory certfac = null;
235         try {
236             certfac = CertificateFactory.getInstance("X.509");
237         } catch (CertificateException ce) {
238             // do nothing
239         }
240 
241         for (int i=0; i < contents.length; i++) {
242             ByteArrayInputStream bais = null;
243             try {
244                 if (certfac == null)
245                     certificates[i] = new X509CertImpl(contents[i]);
246                 else {
247                     byte[] encoded = contents[i].toByteArray();
248                     bais = new ByteArrayInputStream(encoded);
249                     certificates[i] =
250                         (X509Certificate)certfac.generateCertificate(bais);
251                     bais.close();
252                     bais = null;
253                 }
254             } catch (CertificateException ce) {
255                 ParsingException pe = new ParsingException(ce.getMessage());
256                 pe.initCause(ce);
257                 throw pe;
258             } catch (IOException ioe) {
259                 ParsingException pe = new ParsingException(ioe.getMessage());
260                 pe.initCause(ioe);
261                 throw pe;
262             } finally {
263                 if (bais != null)
264                     bais.close();
265             }
266         }
267     }
268 
parseSignedData(DerValue val)269     private void parseSignedData(DerValue val)
270         throws ParsingException, IOException {
271 
272         DerInputStream dis = val.toDerInputStream();
273 
274         // Version
275         version = dis.getBigInteger();
276 
277         // digestAlgorithmIds
278         DerValue[] digestAlgorithmIdVals = dis.getSet(1);
279         int len = digestAlgorithmIdVals.length;
280         digestAlgorithmIds = new AlgorithmId[len];
281         try {
282             for (int i = 0; i < len; i++) {
283                 DerValue oid = digestAlgorithmIdVals[i];
284                 digestAlgorithmIds[i] = AlgorithmId.parse(oid);
285             }
286 
287         } catch (IOException e) {
288             ParsingException pe =
289                 new ParsingException("Error parsing digest AlgorithmId IDs: " +
290                                      e.getMessage());
291             pe.initCause(e);
292             throw pe;
293         }
294         // contentInfo
295         contentInfo = new ContentInfo(dis);
296 
297         CertificateFactory certfac = null;
298         try {
299             certfac = CertificateFactory.getInstance("X.509");
300         } catch (CertificateException ce) {
301             // do nothing
302         }
303 
304         /*
305          * check if certificates (implicit tag) are provided
306          * (certificates are OPTIONAL)
307          */
308         if ((byte)(dis.peekByte()) == (byte)0xA0) {
309             DerValue[] certVals = dis.getSet(2, true);
310 
311             len = certVals.length;
312             certificates = new X509Certificate[len];
313             int count = 0;
314 
315             for (int i = 0; i < len; i++) {
316                 ByteArrayInputStream bais = null;
317                 try {
318                     byte tag = certVals[i].getTag();
319                     // We only parse the normal certificate. Other types of
320                     // CertificateChoices ignored.
321                     if (tag == DerValue.tag_Sequence) {
322                         if (certfac == null) {
323                             certificates[count] = new X509CertImpl(certVals[i]);
324                         } else {
325                             byte[] encoded = certVals[i].toByteArray();
326                             bais = new ByteArrayInputStream(encoded);
327                             certificates[count] =
328                                 (X509Certificate)certfac.generateCertificate(bais);
329                             bais.close();
330                             bais = null;
331                         }
332                         count++;
333                     }
334                 } catch (CertificateException ce) {
335                     ParsingException pe = new ParsingException(ce.getMessage());
336                     pe.initCause(ce);
337                     throw pe;
338                 } catch (IOException ioe) {
339                     ParsingException pe = new ParsingException(ioe.getMessage());
340                     pe.initCause(ioe);
341                     throw pe;
342                 } finally {
343                     if (bais != null)
344                         bais.close();
345                 }
346             }
347             if (count != len) {
348                 certificates = Arrays.copyOf(certificates, count);
349             }
350         }
351 
352         // check if crls (implicit tag) are provided (crls are OPTIONAL)
353         if ((byte)(dis.peekByte()) == (byte)0xA1) {
354             DerValue[] crlVals = dis.getSet(1, true);
355 
356             len = crlVals.length;
357             crls = new X509CRL[len];
358 
359             for (int i = 0; i < len; i++) {
360                 ByteArrayInputStream bais = null;
361                 try {
362                     if (certfac == null)
363                         crls[i] = new X509CRLImpl(crlVals[i]);
364                     else {
365                         byte[] encoded = crlVals[i].toByteArray();
366                         bais = new ByteArrayInputStream(encoded);
367                         crls[i] = (X509CRL) certfac.generateCRL(bais);
368                         bais.close();
369                         bais = null;
370                     }
371                 } catch (CRLException e) {
372                     ParsingException pe =
373                         new ParsingException(e.getMessage());
374                     pe.initCause(e);
375                     throw pe;
376                 } finally {
377                     if (bais != null)
378                         bais.close();
379                 }
380             }
381         }
382 
383         // signerInfos
384         DerValue[] signerInfoVals = dis.getSet(1);
385 
386         len = signerInfoVals.length;
387         signerInfos = new SignerInfo[len];
388 
389         for (int i = 0; i < len; i++) {
390             DerInputStream in = signerInfoVals[i].toDerInputStream();
391             signerInfos[i] = new SignerInfo(in);
392         }
393     }
394 
395     /*
396      * Parses an old-style SignedData encoding (for backwards
397      * compatibility with JDK1.1.x).
398      */
parseOldSignedData(DerValue val)399     private void parseOldSignedData(DerValue val)
400         throws ParsingException, IOException
401     {
402         DerInputStream dis = val.toDerInputStream();
403 
404         // Version
405         version = dis.getBigInteger();
406 
407         // digestAlgorithmIds
408         DerValue[] digestAlgorithmIdVals = dis.getSet(1);
409         int len = digestAlgorithmIdVals.length;
410 
411         digestAlgorithmIds = new AlgorithmId[len];
412         try {
413             for (int i = 0; i < len; i++) {
414                 DerValue oid = digestAlgorithmIdVals[i];
415                 digestAlgorithmIds[i] = AlgorithmId.parse(oid);
416             }
417         } catch (IOException e) {
418             throw new ParsingException("Error parsing digest AlgorithmId IDs");
419         }
420 
421         // contentInfo
422         contentInfo = new ContentInfo(dis, true);
423 
424         // certificates
425         CertificateFactory certfac = null;
426         try {
427             certfac = CertificateFactory.getInstance("X.509");
428         } catch (CertificateException ce) {
429             // do nothing
430         }
431         DerValue[] certVals = dis.getSet(2);
432         len = certVals.length;
433         certificates = new X509Certificate[len];
434 
435         for (int i = 0; i < len; i++) {
436             ByteArrayInputStream bais = null;
437             try {
438                 if (certfac == null)
439                     certificates[i] = new X509CertImpl(certVals[i]);
440                 else {
441                     byte[] encoded = certVals[i].toByteArray();
442                     bais = new ByteArrayInputStream(encoded);
443                     certificates[i] =
444                         (X509Certificate)certfac.generateCertificate(bais);
445                     bais.close();
446                     bais = null;
447                 }
448             } catch (CertificateException ce) {
449                 ParsingException pe = new ParsingException(ce.getMessage());
450                 pe.initCause(ce);
451                 throw pe;
452             } catch (IOException ioe) {
453                 ParsingException pe = new ParsingException(ioe.getMessage());
454                 pe.initCause(ioe);
455                 throw pe;
456             } finally {
457                 if (bais != null)
458                     bais.close();
459             }
460         }
461 
462         // crls are ignored.
463         dis.getSet(0);
464 
465         // signerInfos
466         DerValue[] signerInfoVals = dis.getSet(1);
467         len = signerInfoVals.length;
468         signerInfos = new SignerInfo[len];
469         for (int i = 0; i < len; i++) {
470             DerInputStream in = signerInfoVals[i].toDerInputStream();
471             signerInfos[i] = new SignerInfo(in, true);
472         }
473     }
474 
475     /**
476      * Encodes the signed data to an output stream.
477      *
478      * @param out the output stream to write the encoded data to.
479      * @exception IOException on encoding errors.
480      */
encodeSignedData(OutputStream out)481     public void encodeSignedData(OutputStream out) throws IOException {
482         DerOutputStream derout = new DerOutputStream();
483         encodeSignedData(derout);
484         out.write(derout.toByteArray());
485     }
486 
487     /**
488      * Encodes the signed data to a DerOutputStream.
489      *
490      * @param out the DerOutputStream to write the encoded data to.
491      * @exception IOException on encoding errors.
492      */
encodeSignedData(DerOutputStream out)493     public void encodeSignedData(DerOutputStream out)
494         throws IOException
495     {
496         DerOutputStream signedData = new DerOutputStream();
497 
498         // version
499         signedData.putInteger(version);
500 
501         // digestAlgorithmIds
502         signedData.putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds);
503 
504         // contentInfo
505         contentInfo.encode(signedData);
506 
507         // certificates (optional)
508         if (certificates != null && certificates.length != 0) {
509             // cast to X509CertImpl[] since X509CertImpl implements DerEncoder
510             X509CertImpl[] implCerts = new X509CertImpl[certificates.length];
511             for (int i = 0; i < certificates.length; i++) {
512                 if (certificates[i] instanceof X509CertImpl)
513                     implCerts[i] = (X509CertImpl) certificates[i];
514                 else {
515                     try {
516                         byte[] encoded = certificates[i].getEncoded();
517                         implCerts[i] = new X509CertImpl(encoded);
518                     } catch (CertificateException ce) {
519                         throw new IOException(ce);
520                     }
521                 }
522             }
523 
524             // Add the certificate set (tagged with [0] IMPLICIT)
525             // to the signed data
526             signedData.putOrderedSetOf((byte)0xA0, implCerts);
527         }
528 
529         // CRLs (optional)
530         if (crls != null && crls.length != 0) {
531             // cast to X509CRLImpl[] since X509CRLImpl implements DerEncoder
532             Set<X509CRLImpl> implCRLs = new HashSet<>(crls.length);
533             for (X509CRL crl: crls) {
534                 if (crl instanceof X509CRLImpl)
535                     implCRLs.add((X509CRLImpl) crl);
536                 else {
537                     try {
538                         byte[] encoded = crl.getEncoded();
539                         implCRLs.add(new X509CRLImpl(encoded));
540                     } catch (CRLException ce) {
541                         throw new IOException(ce);
542                     }
543                 }
544             }
545 
546             // Add the CRL set (tagged with [1] IMPLICIT)
547             // to the signed data
548             signedData.putOrderedSetOf((byte)0xA1,
549                     implCRLs.toArray(new X509CRLImpl[implCRLs.size()]));
550         }
551 
552         // signerInfos
553         signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos);
554 
555         // making it a signed data block
556         DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence,
557                                               signedData.toByteArray());
558 
559         // making it a content info sequence
560         ContentInfo block = new ContentInfo(ContentInfo.SIGNED_DATA_OID,
561                                             signedDataSeq);
562 
563         // writing out the contentInfo sequence
564         block.encode(out);
565     }
566 
567     /**
568      * This verifies a given SignerInfo.
569      *
570      * @param info the signer information.
571      * @param bytes the DER encoded content information.
572      *
573      * @exception NoSuchAlgorithmException on unrecognized algorithms.
574      * @exception SignatureException on signature handling errors.
575      */
verify(SignerInfo info, byte[] bytes)576     public SignerInfo verify(SignerInfo info, byte[] bytes)
577     throws NoSuchAlgorithmException, SignatureException {
578         return info.verify(this, bytes);
579     }
580 
581     /**
582      * Returns all signerInfos which self-verify.
583      *
584      * @param bytes the DER encoded content information.
585      *
586      * @exception NoSuchAlgorithmException on unrecognized algorithms.
587      * @exception SignatureException on signature handling errors.
588      */
verify(byte[] bytes)589     public SignerInfo[] verify(byte[] bytes)
590     throws NoSuchAlgorithmException, SignatureException {
591 
592         Vector<SignerInfo> intResult = new Vector<>();
593         for (int i = 0; i < signerInfos.length; i++) {
594 
595             SignerInfo signerInfo = verify(signerInfos[i], bytes);
596             if (signerInfo != null) {
597                 intResult.addElement(signerInfo);
598             }
599         }
600         if (!intResult.isEmpty()) {
601 
602             SignerInfo[] result = new SignerInfo[intResult.size()];
603             intResult.copyInto(result);
604             return result;
605         }
606         return null;
607     }
608 
609     /**
610      * Returns all signerInfos which self-verify.
611      *
612      * @exception NoSuchAlgorithmException on unrecognized algorithms.
613      * @exception SignatureException on signature handling errors.
614      */
verify()615     public SignerInfo[] verify()
616     throws NoSuchAlgorithmException, SignatureException {
617         return verify(null);
618     }
619 
620     /**
621      * Returns the version number of this PKCS7 block.
622      * @return the version or null if version is not specified
623      *         for the content type.
624      */
getVersion()625     public  BigInteger getVersion() {
626         return version;
627     }
628 
629     /**
630      * Returns the message digest algorithms specified in this PKCS7 block.
631      * @return the array of Digest Algorithms or null if none are specified
632      *         for the content type.
633      */
getDigestAlgorithmIds()634     public AlgorithmId[] getDigestAlgorithmIds() {
635         return  digestAlgorithmIds;
636     }
637 
638     /**
639      * Returns the content information specified in this PKCS7 block.
640      */
getContentInfo()641     public ContentInfo getContentInfo() {
642         return contentInfo;
643     }
644 
645     /**
646      * Returns the X.509 certificates listed in this PKCS7 block.
647      * @return a clone of the array of X.509 certificates or null if
648      *         none are specified for the content type.
649      */
getCertificates()650     public X509Certificate[] getCertificates() {
651         if (certificates != null)
652             return certificates.clone();
653         else
654             return null;
655     }
656 
657     /**
658      * Returns the X.509 crls listed in this PKCS7 block.
659      * @return a clone of the array of X.509 crls or null if none
660      *         are specified for the content type.
661      */
getCRLs()662     public X509CRL[] getCRLs() {
663         if (crls != null)
664             return crls.clone();
665         else
666             return null;
667     }
668 
669     /**
670      * Returns the signer's information specified in this PKCS7 block.
671      * @return the array of Signer Infos or null if none are specified
672      *         for the content type.
673      */
getSignerInfos()674     public SignerInfo[] getSignerInfos() {
675         return signerInfos;
676     }
677 
678     /**
679      * Returns the X.509 certificate listed in this PKCS7 block
680      * which has a matching serial number and Issuer name, or
681      * null if one is not found.
682      *
683      * @param serial the serial number of the certificate to retrieve.
684      * @param issuerName the Distinguished Name of the Issuer.
685      */
getCertificate(BigInteger serial, X500Name issuerName)686     public X509Certificate getCertificate(BigInteger serial, X500Name issuerName) {
687         if (certificates != null) {
688             if (certIssuerNames == null)
689                 populateCertIssuerNames();
690             for (int i = 0; i < certificates.length; i++) {
691                 X509Certificate cert = certificates[i];
692                 BigInteger thisSerial = cert.getSerialNumber();
693                 if (serial.equals(thisSerial)
694                     && issuerName.equals(certIssuerNames[i]))
695                 {
696                     return cert;
697                 }
698             }
699         }
700         return null;
701     }
702 
703     /**
704      * Populate array of Issuer DNs from certificates and convert
705      * each Principal to type X500Name if necessary.
706      */
populateCertIssuerNames()707     private void populateCertIssuerNames() {
708         if (certificates == null)
709             return;
710 
711         certIssuerNames = new Principal[certificates.length];
712         for (int i = 0; i < certificates.length; i++) {
713             X509Certificate cert = certificates[i];
714             Principal certIssuerName = cert.getIssuerDN();
715             if (!(certIssuerName instanceof X500Name)) {
716                 // must extract the original encoded form of DN for
717                 // subsequent name comparison checks (converting to a
718                 // String and back to an encoded DN could cause the
719                 // types of String attribute values to be changed)
720                 try {
721                     X509CertInfo tbsCert =
722                         new X509CertInfo(cert.getTBSCertificate());
723                     certIssuerName = (Principal)
724                         tbsCert.get(X509CertInfo.ISSUER + "." +
725                                     X509CertInfo.DN_NAME);
726                 } catch (Exception e) {
727                     // error generating X500Name object from the cert's
728                     // issuer DN, leave name as is.
729                 }
730             }
731             certIssuerNames[i] = certIssuerName;
732         }
733     }
734 
735     /**
736      * Returns the PKCS7 block in a printable string form.
737      */
toString()738     public String toString() {
739         String out = "";
740 
741         out += contentInfo + "\n";
742         if (version != null)
743             out += "PKCS7 :: version: " + Debug.toHexString(version) + "\n";
744         if (digestAlgorithmIds != null) {
745             out += "PKCS7 :: digest AlgorithmIds: \n";
746             for (int i = 0; i < digestAlgorithmIds.length; i++)
747                 out += "\t" + digestAlgorithmIds[i] + "\n";
748         }
749         if (certificates != null) {
750             out += "PKCS7 :: certificates: \n";
751             for (int i = 0; i < certificates.length; i++)
752                 out += "\t" + i + ".   " + certificates[i] + "\n";
753         }
754         if (crls != null) {
755             out += "PKCS7 :: crls: \n";
756             for (int i = 0; i < crls.length; i++)
757                 out += "\t" + i + ".   " + crls[i] + "\n";
758         }
759         if (signerInfos != null) {
760             out += "PKCS7 :: signer infos: \n";
761             for (int i = 0; i < signerInfos.length; i++)
762                 out += ("\t" + i + ".  " + signerInfos[i] + "\n");
763         }
764         return out;
765     }
766 
767     /**
768      * Returns true if this is a JDK1.1.x-style PKCS#7 block, and false
769      * otherwise.
770      */
isOldStyle()771     public boolean isOldStyle() {
772         return this.oldStyle;
773     }
774 
775     /**
776      * Assembles a PKCS #7 signed data message that optionally includes a
777      * signature timestamp.
778      *
779      * @param signature the signature bytes
780      * @param signerChain the signer's X.509 certificate chain
781      * @param content the content that is signed; specify null to not include
782      *        it in the PKCS7 data
783      * @param signatureAlgorithm the name of the signature algorithm
784      * @param tsaURI the URI of the Timestamping Authority; or null if no
785      *         timestamp is requested
786      * @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a
787      *         numerical object identifier; or null if we leave the TSA server
788      *         to choose one. This argument is only used when tsaURI is provided
789      * @return the bytes of the encoded PKCS #7 signed data message
790      * @throws NoSuchAlgorithmException The exception is thrown if the signature
791      *         algorithm is unrecognised.
792      * @throws CertificateException The exception is thrown if an error occurs
793      *         while processing the signer's certificate or the TSA's
794      *         certificate.
795      * @throws IOException The exception is thrown if an error occurs while
796      *         generating the signature timestamp or while generating the signed
797      *         data message.
798      */
generateSignedData(byte[] signature, X509Certificate[] signerChain, byte[] content, String signatureAlgorithm, URI tsaURI, String tSAPolicyID, String tSADigestAlg)799     public static byte[] generateSignedData(byte[] signature,
800                                             X509Certificate[] signerChain,
801                                             byte[] content,
802                                             String signatureAlgorithm,
803                                             URI tsaURI,
804                                             String tSAPolicyID,
805                                             String tSADigestAlg)
806         throws CertificateException, IOException, NoSuchAlgorithmException
807     {
808 
809         // Generate the timestamp token
810         PKCS9Attributes unauthAttrs = null;
811         if (tsaURI != null) {
812             // Timestamp the signature
813             HttpTimestamper tsa = new HttpTimestamper(tsaURI);
814             byte[] tsToken = generateTimestampToken(
815                     tsa, tSAPolicyID, tSADigestAlg, signature);
816 
817             // Insert the timestamp token into the PKCS #7 signer info element
818             // (as an unsigned attribute)
819             unauthAttrs =
820                 new PKCS9Attributes(new PKCS9Attribute[]{
821                     new PKCS9Attribute(
822                         PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID,
823                         tsToken)});
824         }
825 
826         // Create the SignerInfo
827         X500Name issuerName =
828             X500Name.asX500Name(signerChain[0].getIssuerX500Principal());
829         BigInteger serialNumber = signerChain[0].getSerialNumber();
830         String encAlg = AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm);
831         String digAlg = AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm);
832         if (digAlg == null) {
833             throw new UnsupportedOperationException("Unable to determine " +
834                     "the digest algorithm from the signature algorithm.");
835         }
836         SignerInfo signerInfo = new SignerInfo(issuerName, serialNumber,
837                                                AlgorithmId.get(digAlg), null,
838                                                AlgorithmId.get(encAlg),
839                                                signature, unauthAttrs);
840 
841         // Create the PKCS #7 signed data message
842         SignerInfo[] signerInfos = {signerInfo};
843         AlgorithmId[] algorithms = {signerInfo.getDigestAlgorithmId()};
844         // Include or exclude content
845         ContentInfo contentInfo = (content == null)
846             ? new ContentInfo(ContentInfo.DATA_OID, null)
847             : new ContentInfo(content);
848         PKCS7 pkcs7 = new PKCS7(algorithms, contentInfo,
849                                 signerChain, signerInfos);
850         ByteArrayOutputStream p7out = new ByteArrayOutputStream();
851         pkcs7.encodeSignedData(p7out);
852 
853         return p7out.toByteArray();
854     }
855 
856     /**
857      * Requests, processes and validates a timestamp token from a TSA using
858      * common defaults. Uses the following defaults in the timestamp request:
859      * SHA-1 for the hash algorithm, a 64-bit nonce, and request certificate
860      * set to true.
861      *
862      * @param tsa the timestamping authority to use
863      * @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a
864      *         numerical object identifier; or null if we leave the TSA server
865      *         to choose one
866      * @param toBeTimestamped the token that is to be timestamped
867      * @return the encoded timestamp token
868      * @throws IOException The exception is thrown if an error occurs while
869      *                     communicating with the TSA, or a non-null
870      *                     TSAPolicyID is specified in the request but it
871      *                     does not match the one in the reply
872      * @throws CertificateException The exception is thrown if the TSA's
873      *                     certificate is not permitted for timestamping.
874      */
generateTimestampToken(Timestamper tsa, String tSAPolicyID, String tSADigestAlg, byte[] toBeTimestamped)875     private static byte[] generateTimestampToken(Timestamper tsa,
876                                                  String tSAPolicyID,
877                                                  String tSADigestAlg,
878                                                  byte[] toBeTimestamped)
879         throws IOException, CertificateException
880     {
881         // Generate a timestamp
882         MessageDigest messageDigest = null;
883         TSRequest tsQuery = null;
884         try {
885             messageDigest = MessageDigest.getInstance(tSADigestAlg);
886             tsQuery = new TSRequest(tSAPolicyID, toBeTimestamped, messageDigest);
887         } catch (NoSuchAlgorithmException e) {
888             throw new IllegalArgumentException(e);
889         }
890 
891         // Generate a nonce
892         BigInteger nonce = null;
893         if (SecureRandomHolder.RANDOM != null) {
894             nonce = new BigInteger(64, SecureRandomHolder.RANDOM);
895             tsQuery.setNonce(nonce);
896         }
897         tsQuery.requestCertificate(true);
898 
899         TSResponse tsReply = tsa.generateTimestamp(tsQuery);
900         int status = tsReply.getStatusCode();
901         // Handle TSP error
902         if (status != 0 && status != 1) {
903             throw new IOException("Error generating timestamp: " +
904                 tsReply.getStatusCodeAsText() + " " +
905                 tsReply.getFailureCodeAsText());
906         }
907 
908         if (tSAPolicyID != null &&
909                 !tSAPolicyID.equals(tsReply.getTimestampToken().getPolicyID())) {
910             throw new IOException("TSAPolicyID changed in "
911                     + "timestamp token");
912         }
913         PKCS7 tsToken = tsReply.getToken();
914 
915         TimestampToken tst = tsReply.getTimestampToken();
916         try {
917             if (!tst.getHashAlgorithm().equals(AlgorithmId.get(tSADigestAlg))) {
918                 throw new IOException("Digest algorithm not " + tSADigestAlg + " in "
919                                       + "timestamp token");
920             }
921         } catch (NoSuchAlgorithmException nase) {
922             throw new IllegalArgumentException();   // should have been caught before
923         }
924         if (!MessageDigest.isEqual(tst.getHashedMessage(),
925                                    tsQuery.getHashedMessage())) {
926             throw new IOException("Digest octets changed in timestamp token");
927         }
928 
929         BigInteger replyNonce = tst.getNonce();
930         if (replyNonce == null && nonce != null) {
931             throw new IOException("Nonce missing in timestamp token");
932         }
933         if (replyNonce != null && !replyNonce.equals(nonce)) {
934             throw new IOException("Nonce changed in timestamp token");
935         }
936 
937         // Examine the TSA's certificate (if present)
938         for (SignerInfo si: tsToken.getSignerInfos()) {
939             X509Certificate cert = si.getCertificate(tsToken);
940             if (cert == null) {
941                 // Error, we've already set tsRequestCertificate = true
942                 throw new CertificateException(
943                 "Certificate not included in timestamp token");
944             } else {
945                 if (!cert.getCriticalExtensionOIDs().contains(
946                         EXTENDED_KEY_USAGE_OID)) {
947                     throw new CertificateException(
948                     "Certificate is not valid for timestamping");
949                 }
950                 List<String> keyPurposes = cert.getExtendedKeyUsage();
951                 if (keyPurposes == null ||
952                         !keyPurposes.contains(KP_TIMESTAMPING_OID)) {
953                     throw new CertificateException(
954                     "Certificate is not valid for timestamping");
955                 }
956             }
957         }
958         return tsReply.getEncodedToken();
959     }
960 }
961