1/*
2 * Copyright (c) 1996, 2015, 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
26package sun.security.pkcs;
27
28import java.io.*;
29import java.math.BigInteger;
30import java.net.URI;
31import java.util.*;
32import java.security.cert.X509Certificate;
33import java.security.cert.CertificateException;
34import java.security.cert.X509CRL;
35import java.security.cert.CRLException;
36import java.security.cert.CertificateFactory;
37import java.security.*;
38
39import sun.security.timestamp.*;
40import sun.security.util.*;
41import sun.security.x509.AlgorithmId;
42import sun.security.x509.X509CertImpl;
43import sun.security.x509.X509CertInfo;
44import sun.security.x509.X509CRLImpl;
45import 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 */
56public 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(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     */
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
221    public PKCS7(AlgorithmId[] digestAlgorithmIds,
222                 ContentInfo contentInfo,
223                 X509Certificate[] certificates,
224                 SignerInfo[] signerInfos) {
225        this(digestAlgorithmIds, contentInfo, certificates, null, signerInfos);
226    }
227
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
634    public AlgorithmId[] getDigestAlgorithmIds() {
635        return  digestAlgorithmIds;
636    }
637
638    /**
639     * Returns the content information specified in this PKCS7 block.
640     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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_STR,
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        SignerInfo signerInfo = new SignerInfo(issuerName, serialNumber,
833                                               AlgorithmId.get(digAlg), null,
834                                               AlgorithmId.get(encAlg),
835                                               signature, unauthAttrs);
836
837        // Create the PKCS #7 signed data message
838        SignerInfo[] signerInfos = {signerInfo};
839        AlgorithmId[] algorithms = {signerInfo.getDigestAlgorithmId()};
840        // Include or exclude content
841        ContentInfo contentInfo = (content == null)
842            ? new ContentInfo(ContentInfo.DATA_OID, null)
843            : new ContentInfo(content);
844        PKCS7 pkcs7 = new PKCS7(algorithms, contentInfo,
845                                signerChain, signerInfos);
846        ByteArrayOutputStream p7out = new ByteArrayOutputStream();
847        pkcs7.encodeSignedData(p7out);
848
849        return p7out.toByteArray();
850    }
851
852    /**
853     * Requests, processes and validates a timestamp token from a TSA using
854     * common defaults. Uses the following defaults in the timestamp request:
855     * SHA-1 for the hash algorithm, a 64-bit nonce, and request certificate
856     * set to true.
857     *
858     * @param tsa the timestamping authority to use
859     * @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a
860     *         numerical object identifier; or null if we leave the TSA server
861     *         to choose one
862     * @param toBeTimestamped the token that is to be timestamped
863     * @return the encoded timestamp token
864     * @throws IOException The exception is thrown if an error occurs while
865     *                     communicating with the TSA, or a non-null
866     *                     TSAPolicyID is specified in the request but it
867     *                     does not match the one in the reply
868     * @throws CertificateException The exception is thrown if the TSA's
869     *                     certificate is not permitted for timestamping.
870     */
871    private static byte[] generateTimestampToken(Timestamper tsa,
872                                                 String tSAPolicyID,
873                                                 String tSADigestAlg,
874                                                 byte[] toBeTimestamped)
875        throws IOException, CertificateException
876    {
877        // Generate a timestamp
878        MessageDigest messageDigest = null;
879        TSRequest tsQuery = null;
880        try {
881            messageDigest = MessageDigest.getInstance(tSADigestAlg);
882            tsQuery = new TSRequest(tSAPolicyID, toBeTimestamped, messageDigest);
883        } catch (NoSuchAlgorithmException e) {
884            throw new IllegalArgumentException(e);
885        }
886
887        // Generate a nonce
888        BigInteger nonce = null;
889        if (SecureRandomHolder.RANDOM != null) {
890            nonce = new BigInteger(64, SecureRandomHolder.RANDOM);
891            tsQuery.setNonce(nonce);
892        }
893        tsQuery.requestCertificate(true);
894
895        TSResponse tsReply = tsa.generateTimestamp(tsQuery);
896        int status = tsReply.getStatusCode();
897        // Handle TSP error
898        if (status != 0 && status != 1) {
899            throw new IOException("Error generating timestamp: " +
900                tsReply.getStatusCodeAsText() + " " +
901                tsReply.getFailureCodeAsText());
902        }
903
904        if (tSAPolicyID != null &&
905                !tSAPolicyID.equals(tsReply.getTimestampToken().getPolicyID())) {
906            throw new IOException("TSAPolicyID changed in "
907                    + "timestamp token");
908        }
909        PKCS7 tsToken = tsReply.getToken();
910
911        TimestampToken tst = tsReply.getTimestampToken();
912        try {
913            if (!tst.getHashAlgorithm().equals(AlgorithmId.get(tSADigestAlg))) {
914                throw new IOException("Digest algorithm not " + tSADigestAlg + " in "
915                                      + "timestamp token");
916            }
917        } catch (NoSuchAlgorithmException nase) {
918            throw new IllegalArgumentException();   // should have been caught before
919        }
920        if (!MessageDigest.isEqual(tst.getHashedMessage(),
921                                   tsQuery.getHashedMessage())) {
922            throw new IOException("Digest octets changed in timestamp token");
923        }
924
925        BigInteger replyNonce = tst.getNonce();
926        if (replyNonce == null && nonce != null) {
927            throw new IOException("Nonce missing in timestamp token");
928        }
929        if (replyNonce != null && !replyNonce.equals(nonce)) {
930            throw new IOException("Nonce changed in timestamp token");
931        }
932
933        // Examine the TSA's certificate (if present)
934        for (SignerInfo si: tsToken.getSignerInfos()) {
935            X509Certificate cert = si.getCertificate(tsToken);
936            if (cert == null) {
937                // Error, we've already set tsRequestCertificate = true
938                throw new CertificateException(
939                "Certificate not included in timestamp token");
940            } else {
941                if (!cert.getCriticalExtensionOIDs().contains(
942                        EXTENDED_KEY_USAGE_OID)) {
943                    throw new CertificateException(
944                    "Certificate is not valid for timestamping");
945                }
946                List<String> keyPurposes = cert.getExtendedKeyUsage();
947                if (keyPurposes == null ||
948                        !keyPurposes.contains(KP_TIMESTAMPING_OID)) {
949                    throw new CertificateException(
950                    "Certificate is not valid for timestamping");
951                }
952            }
953        }
954        return tsReply.getEncodedToken();
955    }
956}
957