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 
  26 package sun.security.pkcs;
  27 
  28 import java.io.*;
  29 import java.math.BigInteger;
  30 import java.net.URI;
  31 import java.util.*;
  32 import java.security.cert.X509Certificate;
  33 import java.security.cert.CertificateException;
  34 import java.security.cert.X509CRL;
  35 import java.security.cert.CRLException;
  36 import java.security.cert.CertificateFactory;
  37 import java.security.*;
  38 
  39 import sun.security.timestamp.*;
  40 import sun.security.util.*;
  41 import sun.security.x509.AlgorithmId;
  42 import sun.security.x509.X509CertImpl;
  43 import sun.security.x509.X509CertInfo;
  44 import sun.security.x509.X509CRLImpl;
  45 import sun.security.x509.X500Name;
  46 
  47 /**
  48  * PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile
  49  * Supports only {@code SignedData} ContentInfo
  50  * type, where to the type of data signed is plain Data.
  51  * For signedData, {@code crls}, {@code attributes} and
  52  * PKCS#6 Extended Certificates are not supported.
  53  *
  54  * @author Benjamin Renaud
  55  */
  56 public class PKCS7 {
  57 
  58     private ObjectIdentifier contentType;
  59 
  60     // the ASN.1 members for a signedData (and other) contentTypes
  61     private BigInteger version = null;
  62     private AlgorithmId[] digestAlgorithmIds = null;
  63     private ContentInfo contentInfo = null;
  64     private X509Certificate[] certificates = null;
  65     private X509CRL[] crls = null;
  66     private SignerInfo[] signerInfos = null;
  67 
  68     private boolean oldStyle = false; // Is this JDK1.1.x-style?
  69 
  70     private Principal[] certIssuerNames;
  71 
  72     /*
  73      * Random number generator for creating nonce values
  74      * (Lazy initialization)
  75      */
  76     private static class SecureRandomHolder {
  77         static final SecureRandom RANDOM;
  78         static {
  79             SecureRandom tmp = null;
  80             try {
  81                 tmp = SecureRandom.getInstance("SHA1PRNG");
  82             } catch (NoSuchAlgorithmException e) {
  83                 // should not happen
  84             }
  85             RANDOM = tmp;
  86         }
  87     }
  88 
  89     /*
  90      * Object identifier for the timestamping key purpose.
  91      */
  92     private static final String KP_TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8";
  93 
  94     /*
  95      * Object identifier for extendedKeyUsage extension
  96      */
  97     private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37";
  98 
  99     /**
 100      * Unmarshals a PKCS7 block from its encoded form, parsing the
 101      * encoded bytes from the InputStream.
 102      *
 103      * @param in an input stream holding at least one PKCS7 block.
 104      * @exception ParsingException on parsing errors.
 105      * @exception IOException on other errors.
 106      */
 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 }