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