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                                             String tSADigestAlg)
 807         throws CertificateException, IOException, NoSuchAlgorithmException
 808     {
 809 
 810         // Generate the timestamp token
 811         PKCS9Attributes unauthAttrs = null;
 812         if (tsaURI != null) {
 813             // Timestamp the signature
 814             HttpTimestamper tsa = new HttpTimestamper(tsaURI);
 815             byte[] tsToken = generateTimestampToken(
 816                     tsa, tSAPolicyID, tSADigestAlg, signature);
 817 
 818             // Insert the timestamp token into the PKCS #7 signer info element
 819             // (as an unsigned attribute)
 820             unauthAttrs =
 821                 new PKCS9Attributes(new PKCS9Attribute[]{
 822                     new PKCS9Attribute(
 823                         PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_STR,
 824                         tsToken)});
 825         }
 826 
 827         // Create the SignerInfo
 828         X500Name issuerName =
 829             X500Name.asX500Name(signerChain[0].getIssuerX500Principal());
 830         BigInteger serialNumber = signerChain[0].getSerialNumber();
 831         String encAlg = AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm);
 832         String digAlg = AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm);
 833         SignerInfo signerInfo = new SignerInfo(issuerName, serialNumber,
 834                                                AlgorithmId.get(digAlg), null,
 835                                                AlgorithmId.get(encAlg),
 836                                                signature, unauthAttrs);
 837 
 838         // Create the PKCS #7 signed data message
 839         SignerInfo[] signerInfos = {signerInfo};
 840         AlgorithmId[] algorithms = {signerInfo.getDigestAlgorithmId()};
 841         // Include or exclude content
 842         ContentInfo contentInfo = (content == null)
 843             ? new ContentInfo(ContentInfo.DATA_OID, null)
 844             : new ContentInfo(content);
 845         PKCS7 pkcs7 = new PKCS7(algorithms, contentInfo,
 846                                 signerChain, signerInfos);
 847         ByteArrayOutputStream p7out = new ByteArrayOutputStream();
 848         pkcs7.encodeSignedData(p7out);
 849 
 850         return p7out.toByteArray();
 851     }
 852 
 853     /**
 854      * Requests, processes and validates a timestamp token from a TSA using
 855      * common defaults. Uses the following defaults in the timestamp request:
 856      * SHA-1 for the hash algorithm, a 64-bit nonce, and request certificate
 857      * set to true.
 858      *
 859      * @param tsa the timestamping authority to use
 860      * @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a
 861      *         numerical object identifier; or null if we leave the TSA server
 862      *         to choose one
 863      * @param toBeTimestamped the token that is to be timestamped
 864      * @return the encoded timestamp token
 865      * @throws IOException The exception is thrown if an error occurs while
 866      *                     communicating with the TSA, or a non-null
 867      *                     TSAPolicyID is specified in the request but it
 868      *                     does not match the one in the reply
 869      * @throws CertificateException The exception is thrown if the TSA's
 870      *                     certificate is not permitted for timestamping.
 871      */
 872     private static byte[] generateTimestampToken(Timestamper tsa,
 873                                                  String tSAPolicyID,
 874                                                  String tSADigestAlg,
 875                                                  byte[] toBeTimestamped)
 876         throws IOException, CertificateException
 877     {
 878         // Generate a timestamp
 879         MessageDigest messageDigest = null;
 880         TSRequest tsQuery = null;
 881         try {
 882             messageDigest = MessageDigest.getInstance(tSADigestAlg);
 883             tsQuery = new TSRequest(tSAPolicyID, toBeTimestamped, messageDigest);
 884         } catch (NoSuchAlgorithmException e) {
 885             throw new IllegalArgumentException(e);
 886         }
 887 
 888         // Generate a nonce
 889         BigInteger nonce = null;
 890         if (SecureRandomHolder.RANDOM != null) {
 891             nonce = new BigInteger(64, SecureRandomHolder.RANDOM);
 892             tsQuery.setNonce(nonce);
 893         }
 894         tsQuery.requestCertificate(true);
 895 
 896         TSResponse tsReply = tsa.generateTimestamp(tsQuery);
 897         int status = tsReply.getStatusCode();
 898         // Handle TSP error
 899         if (status != 0 && status != 1) {
 900             throw new IOException("Error generating timestamp: " +
 901                 tsReply.getStatusCodeAsText() + " " +
 902                 tsReply.getFailureCodeAsText());
 903         }
 904 
 905         if (tSAPolicyID != null &&
 906                 !tSAPolicyID.equals(tsReply.getTimestampToken().getPolicyID())) {
 907             throw new IOException("TSAPolicyID changed in "
 908                     + "timestamp token");
 909         }
 910         PKCS7 tsToken = tsReply.getToken();
 911 
 912         TimestampToken tst = tsReply.getTimestampToken();
 913         try {
 914             if (!tst.getHashAlgorithm().equals(AlgorithmId.get(tSADigestAlg))) {
 915                 throw new IOException("Digest algorithm not " + tSADigestAlg + " in "
 916                                       + "timestamp token");
 917             }
 918         } catch (NoSuchAlgorithmException nase) {
 919             throw new IllegalArgumentException();   // should have been caught before
 920         }
 921         if (!MessageDigest.isEqual(tst.getHashedMessage(),
 922                                    tsQuery.getHashedMessage())) {
 923             throw new IOException("Digest octets changed in timestamp token");
 924         }
 925 
 926         BigInteger replyNonce = tst.getNonce();
 927         if (replyNonce == null && nonce != null) {
 928             throw new IOException("Nonce missing in timestamp token");
 929         }
 930         if (replyNonce != null && !replyNonce.equals(nonce)) {
 931             throw new IOException("Nonce changed in timestamp token");
 932         }
 933 
 934         // Examine the TSA's certificate (if present)
 935         for (SignerInfo si: tsToken.getSignerInfos()) {
 936             X509Certificate cert = si.getCertificate(tsToken);
 937             if (cert == null) {
 938                 // Error, we've already set tsRequestCertificate = true
 939                 throw new CertificateException(
 940                 "Certificate not included in timestamp token");
 941             } else {
 942                 if (!cert.getCriticalExtensionOIDs().contains(
 943                         EXTENDED_KEY_USAGE_OID)) {
 944                     throw new CertificateException(
 945                     "Certificate is not valid for timestamping");
 946                 }
 947                 List<String> keyPurposes = cert.getExtendedKeyUsage();
 948                 if (keyPurposes == null ||
 949                         !keyPurposes.contains(KP_TIMESTAMPING_OID)) {
 950                     throw new CertificateException(
 951                     "Certificate is not valid for timestamping");
 952                 }
 953             }
 954         }
 955         return tsReply.getEncodedToken();
 956     }
 957 }