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