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 }