1 /* 2 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.pkcs; 27 28 import java.io.*; 29 import java.math.BigInteger; 30 import java.net.URI; 31 import java.util.*; 32 import java.security.cert.X509Certificate; 33 import java.security.cert.CertificateException; 34 import java.security.cert.X509CRL; 35 import java.security.cert.CRLException; 36 import java.security.cert.CertificateFactory; 37 import java.security.*; 38 39 import sun.security.timestamp.*; 40 import sun.security.util.*; 41 import sun.security.x509.AlgorithmId; 42 import sun.security.x509.X509CertImpl; 43 import sun.security.x509.X509CertInfo; 44 import sun.security.x509.X509CRLImpl; 45 import sun.security.x509.X500Name; 46 47 /** 48 * PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile 49 * Supports only <tt>SignedData</tt> ContentInfo 50 * type, where to the type of data signed is plain Data. 51 * For signedData, <tt>crls</tt>, <tt>attributes</tt> and 52 * PKCS#6 Extended Certificates are not supported. 53 * 54 * @author Benjamin Renaud 55 */ 56 public class PKCS7 { 57 58 private ObjectIdentifier contentType; 59 60 // the ASN.1 members for a signedData (and other) contentTypes 61 private BigInteger version = null; 62 private AlgorithmId[] digestAlgorithmIds = null; 63 private ContentInfo contentInfo = null; 64 private X509Certificate[] certificates = null; 65 private X509CRL[] crls = null; 66 private SignerInfo[] signerInfos = null; 67 68 private boolean oldStyle = false; // Is this JDK1.1.x-style? 69 70 private Principal[] certIssuerNames; 71 72 /* 73 * Random number generator for creating nonce values 74 * (Lazy initialization) 75 */ 76 private static class SecureRandomHolder { 77 static final SecureRandom RANDOM; 78 static { 79 SecureRandom tmp = null; 80 try { 81 tmp = SecureRandom.getInstance("SHA1PRNG"); 82 } catch (NoSuchAlgorithmException e) { 83 // should not happen 84 } 85 RANDOM = tmp; 86 } 87 } 88 89 /* 90 * Object identifier for the timestamping key purpose. 91 */ 92 private static final String KP_TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8"; 93 94 /* 95 * Object identifier for extendedKeyUsage extension 96 */ 97 private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37"; 98 99 /** 100 * Unmarshals a PKCS7 block from its encoded form, parsing the 101 * encoded bytes from the InputStream. 102 * 103 * @param in an input stream holding at least one PKCS7 block. 104 * @exception ParsingException on parsing errors. 105 * @exception IOException on other errors. 106 */ 107 public PKCS7(InputStream in) throws ParsingException, IOException { 108 DataInputStream dis = new DataInputStream(in); 109 byte[] data = new byte[dis.available()]; 110 dis.readFully(data); 111 112 parse(new DerInputStream(data)); 113 } 114 115 /** 116 * Unmarshals a PKCS7 block from its encoded form, parsing the 117 * encoded bytes from the DerInputStream. 118 * 119 * @param derin a DerInputStream holding at least one PKCS7 block. 120 * @exception ParsingException on parsing errors. 121 */ 122 public PKCS7(DerInputStream derin) throws ParsingException { 123 parse(derin); 124 } 125 126 /** 127 * Unmarshals a PKCS7 block from its encoded form, parsing the 128 * encoded bytes. 129 * 130 * @param bytes the encoded bytes. 131 * @exception ParsingException on parsing errors. 132 */ 133 public PKCS7(byte[] bytes) throws ParsingException { 134 try { 135 DerInputStream derin = new DerInputStream(bytes); 136 parse(derin); 137 } catch (IOException ioe1) { 138 ParsingException pe = new ParsingException( 139 "Unable to parse the encoded bytes"); 140 pe.initCause(ioe1); 141 throw pe; 142 } 143 } 144 145 /* 146 * Parses a PKCS#7 block. 147 */ 148 private void parse(DerInputStream derin) 149 throws ParsingException 150 { 151 try { 152 derin.mark(derin.available()); 153 // try new (i.e., JDK1.2) style 154 parse(derin, false); 155 } catch (IOException ioe) { 156 try { 157 derin.reset(); 158 // try old (i.e., JDK1.1.x) style 159 parse(derin, true); 160 oldStyle = true; 161 } catch (IOException ioe1) { 162 ParsingException pe = new ParsingException( 163 ioe1.getMessage()); 164 pe.initCause(ioe); 165 pe.addSuppressed(ioe1); 166 throw pe; 167 } 168 } 169 } 170 171 /** 172 * Parses a PKCS#7 block. 173 * 174 * @param derin the ASN.1 encoding of the PKCS#7 block. 175 * @param oldStyle flag indicating whether or not the given PKCS#7 block 176 * is encoded according to JDK1.1.x. 177 */ 178 private void parse(DerInputStream derin, boolean oldStyle) 179 throws IOException 180 { 181 contentInfo = new ContentInfo(derin, oldStyle); 182 contentType = contentInfo.contentType; 183 DerValue content = contentInfo.getContent(); 184 185 if (contentType.equals((Object)ContentInfo.SIGNED_DATA_OID)) { 186 parseSignedData(content); 187 } else if (contentType.equals((Object)ContentInfo.OLD_SIGNED_DATA_OID)) { 188 // This is for backwards compatibility with JDK 1.1.x 189 parseOldSignedData(content); 190 } else if (contentType.equals((Object) 191 ContentInfo.NETSCAPE_CERT_SEQUENCE_OID)){ 192 parseNetscapeCertChain(content); 193 } else { 194 throw new ParsingException("content type " + contentType + 195 " not supported."); 196 } 197 } 198 199 /** 200 * Construct an initialized PKCS7 block. 201 * 202 * @param digestAlgorithmIds the message digest algorithm identifiers. 203 * @param contentInfo the content information. 204 * @param certificates an array of X.509 certificates. 205 * @param crls an array of CRLs 206 * @param signerInfos an array of signer information. 207 */ 208 public PKCS7(AlgorithmId[] digestAlgorithmIds, 209 ContentInfo contentInfo, 210 X509Certificate[] certificates, 211 X509CRL[] crls, 212 SignerInfo[] signerInfos) { 213 214 version = BigInteger.ONE; 215 this.digestAlgorithmIds = digestAlgorithmIds; 216 this.contentInfo = contentInfo; 217 this.certificates = certificates; 218 this.crls = crls; 219 this.signerInfos = signerInfos; 220 } 221 222 public PKCS7(AlgorithmId[] digestAlgorithmIds, 223 ContentInfo contentInfo, 224 X509Certificate[] certificates, 225 SignerInfo[] signerInfos) { 226 this(digestAlgorithmIds, contentInfo, certificates, null, signerInfos); 227 } 228 229 private void parseNetscapeCertChain(DerValue val) 230 throws ParsingException, IOException { 231 DerInputStream dis = new DerInputStream(val.toByteArray()); 232 DerValue[] contents = dis.getSequence(2); 233 certificates = new X509Certificate[contents.length]; 234 235 CertificateFactory certfac = null; 236 try { 237 certfac = CertificateFactory.getInstance("X.509"); 238 } catch (CertificateException ce) { 239 // do nothing 240 } 241 242 for (int i=0; i < contents.length; i++) { 243 ByteArrayInputStream bais = null; 244 try { 245 if (certfac == null) 246 certificates[i] = new X509CertImpl(contents[i]); 247 else { 248 byte[] encoded = contents[i].toByteArray(); 249 bais = new ByteArrayInputStream(encoded); 250 certificates[i] = 251 (X509Certificate)certfac.generateCertificate(bais); 252 bais.close(); 253 bais = null; 254 } 255 } catch (CertificateException ce) { 256 ParsingException pe = new ParsingException(ce.getMessage()); 257 pe.initCause(ce); 258 throw pe; 259 } catch (IOException ioe) { 260 ParsingException pe = new ParsingException(ioe.getMessage()); 261 pe.initCause(ioe); 262 throw pe; 263 } finally { 264 if (bais != null) 265 bais.close(); 266 } 267 } 268 } 269 270 private void parseSignedData(DerValue val) 271 throws ParsingException, IOException { 272 273 DerInputStream dis = val.toDerInputStream(); 274 275 // Version 276 version = dis.getBigInteger(); 277 278 // digestAlgorithmIds 279 DerValue[] digestAlgorithmIdVals = dis.getSet(1); 280 int len = digestAlgorithmIdVals.length; 281 digestAlgorithmIds = new AlgorithmId[len]; 282 try { 283 for (int i = 0; i < len; i++) { 284 DerValue oid = digestAlgorithmIdVals[i]; 285 digestAlgorithmIds[i] = AlgorithmId.parse(oid); 286 } 287 288 } catch (IOException e) { 289 ParsingException pe = 290 new ParsingException("Error parsing digest AlgorithmId IDs: " + 291 e.getMessage()); 292 pe.initCause(e); 293 throw pe; 294 } 295 // contentInfo 296 contentInfo = new ContentInfo(dis); 297 298 CertificateFactory certfac = null; 299 try { 300 certfac = CertificateFactory.getInstance("X.509"); 301 } catch (CertificateException ce) { 302 // do nothing 303 } 304 305 /* 306 * check if certificates (implicit tag) are provided 307 * (certificates are OPTIONAL) 308 */ 309 if ((byte)(dis.peekByte()) == (byte)0xA0) { 310 DerValue[] certVals = dis.getSet(2, true); 311 312 len = certVals.length; 313 certificates = new X509Certificate[len]; 314 int count = 0; 315 316 for (int i = 0; i < len; i++) { 317 ByteArrayInputStream bais = null; 318 try { 319 byte tag = certVals[i].getTag(); 320 // We only parse the normal certificate. Other types of 321 // CertificateChoices ignored. 322 if (tag == DerValue.tag_Sequence) { 323 if (certfac == null) { 324 certificates[count] = new X509CertImpl(certVals[i]); 325 } else { 326 byte[] encoded = certVals[i].toByteArray(); 327 bais = new ByteArrayInputStream(encoded); 328 certificates[count] = 329 (X509Certificate)certfac.generateCertificate(bais); 330 bais.close(); 331 bais = null; 332 } 333 count++; 334 } 335 } catch (CertificateException ce) { 336 ParsingException pe = new ParsingException(ce.getMessage()); 337 pe.initCause(ce); 338 throw pe; 339 } catch (IOException ioe) { 340 ParsingException pe = new ParsingException(ioe.getMessage()); 341 pe.initCause(ioe); 342 throw pe; 343 } finally { 344 if (bais != null) 345 bais.close(); 346 } 347 } 348 if (count != len) { 349 certificates = Arrays.copyOf(certificates, count); 350 } 351 } 352 353 // check if crls (implicit tag) are provided (crls are OPTIONAL) 354 if ((byte)(dis.peekByte()) == (byte)0xA1) { 355 DerValue[] crlVals = dis.getSet(1, true); 356 357 len = crlVals.length; 358 crls = new X509CRL[len]; 359 360 for (int i = 0; i < len; i++) { 361 ByteArrayInputStream bais = null; 362 try { 363 if (certfac == null) 364 crls[i] = new X509CRLImpl(crlVals[i]); 365 else { 366 byte[] encoded = crlVals[i].toByteArray(); 367 bais = new ByteArrayInputStream(encoded); 368 crls[i] = (X509CRL) certfac.generateCRL(bais); 369 bais.close(); 370 bais = null; 371 } 372 } catch (CRLException e) { 373 ParsingException pe = 374 new ParsingException(e.getMessage()); 375 pe.initCause(e); 376 throw pe; 377 } finally { 378 if (bais != null) 379 bais.close(); 380 } 381 } 382 } 383 384 // signerInfos 385 DerValue[] signerInfoVals = dis.getSet(1); 386 387 len = signerInfoVals.length; 388 signerInfos = new SignerInfo[len]; 389 390 for (int i = 0; i < len; i++) { 391 DerInputStream in = signerInfoVals[i].toDerInputStream(); 392 signerInfos[i] = new SignerInfo(in); 393 } 394 } 395 396 /* 397 * Parses an old-style SignedData encoding (for backwards 398 * compatibility with JDK1.1.x). 399 */ 400 private void parseOldSignedData(DerValue val) 401 throws ParsingException, IOException 402 { 403 DerInputStream dis = val.toDerInputStream(); 404 405 // Version 406 version = dis.getBigInteger(); 407 408 // digestAlgorithmIds 409 DerValue[] digestAlgorithmIdVals = dis.getSet(1); 410 int len = digestAlgorithmIdVals.length; 411 412 digestAlgorithmIds = new AlgorithmId[len]; 413 try { 414 for (int i = 0; i < len; i++) { 415 DerValue oid = digestAlgorithmIdVals[i]; 416 digestAlgorithmIds[i] = AlgorithmId.parse(oid); 417 } 418 } catch (IOException e) { 419 throw new ParsingException("Error parsing digest AlgorithmId IDs"); 420 } 421 422 // contentInfo 423 contentInfo = new ContentInfo(dis, true); 424 425 // certificates 426 CertificateFactory certfac = null; 427 try { 428 certfac = CertificateFactory.getInstance("X.509"); 429 } catch (CertificateException ce) { 430 // do nothing 431 } 432 DerValue[] certVals = dis.getSet(2); 433 len = certVals.length; 434 certificates = new X509Certificate[len]; 435 436 for (int i = 0; i < len; i++) { 437 ByteArrayInputStream bais = null; 438 try { 439 if (certfac == null) 440 certificates[i] = new X509CertImpl(certVals[i]); 441 else { 442 byte[] encoded = certVals[i].toByteArray(); 443 bais = new ByteArrayInputStream(encoded); 444 certificates[i] = 445 (X509Certificate)certfac.generateCertificate(bais); 446 bais.close(); 447 bais = null; 448 } 449 } catch (CertificateException ce) { 450 ParsingException pe = new ParsingException(ce.getMessage()); 451 pe.initCause(ce); 452 throw pe; 453 } catch (IOException ioe) { 454 ParsingException pe = new ParsingException(ioe.getMessage()); 455 pe.initCause(ioe); 456 throw pe; 457 } finally { 458 if (bais != null) 459 bais.close(); 460 } 461 } 462 463 // crls are ignored. 464 dis.getSet(0); 465 466 // signerInfos 467 DerValue[] signerInfoVals = dis.getSet(1); 468 len = signerInfoVals.length; 469 signerInfos = new SignerInfo[len]; 470 for (int i = 0; i < len; i++) { 471 DerInputStream in = signerInfoVals[i].toDerInputStream(); 472 signerInfos[i] = new SignerInfo(in, true); 473 } 474 } 475 476 /** 477 * Encodes the signed data to an output stream. 478 * 479 * @param out the output stream to write the encoded data to. 480 * @exception IOException on encoding errors. 481 */ 482 public void encodeSignedData(OutputStream out) throws IOException { 483 DerOutputStream derout = new DerOutputStream(); 484 encodeSignedData(derout); 485 out.write(derout.toByteArray()); 486 } 487 488 /** 489 * Encodes the signed data to a DerOutputStream. 490 * 491 * @param out the DerOutputStream to write the encoded data to. 492 * @exception IOException on encoding errors. 493 */ 494 public void encodeSignedData(DerOutputStream out) 495 throws IOException 496 { 497 DerOutputStream signedData = new DerOutputStream(); 498 499 // version 500 signedData.putInteger(version); 501 502 // digestAlgorithmIds 503 signedData.putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds); 504 505 // contentInfo 506 contentInfo.encode(signedData); 507 508 // certificates (optional) 509 if (certificates != null && certificates.length != 0) { 510 // cast to X509CertImpl[] since X509CertImpl implements DerEncoder 511 X509CertImpl implCerts[] = new X509CertImpl[certificates.length]; 512 for (int i = 0; i < certificates.length; i++) { 513 if (certificates[i] instanceof X509CertImpl) 514 implCerts[i] = (X509CertImpl) certificates[i]; 515 else { 516 try { 517 byte[] encoded = certificates[i].getEncoded(); 518 implCerts[i] = new X509CertImpl(encoded); 519 } catch (CertificateException ce) { 520 throw new IOException(ce); 521 } 522 } 523 } 524 525 // Add the certificate set (tagged with [0] IMPLICIT) 526 // to the signed data 527 signedData.putOrderedSetOf((byte)0xA0, implCerts); 528 } 529 530 // CRLs (optional) 531 if (crls != null && crls.length != 0) { 532 // cast to X509CRLImpl[] since X509CRLImpl implements DerEncoder 533 Set<X509CRLImpl> implCRLs = new HashSet<X509CRLImpl>(crls.length); 534 for (X509CRL crl: crls) { 535 if (crl instanceof X509CRLImpl) 536 implCRLs.add((X509CRLImpl) crl); 537 else { 538 try { 539 byte[] encoded = crl.getEncoded(); 540 implCRLs.add(new X509CRLImpl(encoded)); 541 } catch (CRLException ce) { 542 throw new IOException(ce); 543 } 544 } 545 } 546 547 // Add the CRL set (tagged with [1] IMPLICIT) 548 // to the signed data 549 signedData.putOrderedSetOf((byte)0xA1, 550 implCRLs.toArray(new X509CRLImpl[implCRLs.size()])); 551 } 552 553 // signerInfos 554 signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos); 555 556 // making it a signed data block 557 DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence, 558 signedData.toByteArray()); 559 560 // making it a content info sequence 561 ContentInfo block = new ContentInfo(ContentInfo.SIGNED_DATA_OID, 562 signedDataSeq); 563 564 // writing out the contentInfo sequence 565 block.encode(out); 566 } 567 568 /** 569 * This verifies a given SignerInfo. 570 * 571 * @param info the signer information. 572 * @param bytes the DER encoded content information. 573 * 574 * @exception NoSuchAlgorithmException on unrecognized algorithms. 575 * @exception SignatureException on signature handling errors. 576 */ 577 public SignerInfo verify(SignerInfo info, byte[] bytes) 578 throws NoSuchAlgorithmException, SignatureException { 579 return info.verify(this, bytes); 580 } 581 582 /** 583 * Returns all signerInfos which self-verify. 584 * 585 * @param bytes the DER encoded content information. 586 * 587 * @exception NoSuchAlgorithmException on unrecognized algorithms. 588 * @exception SignatureException on signature handling errors. 589 */ 590 public SignerInfo[] verify(byte[] bytes) 591 throws NoSuchAlgorithmException, SignatureException { 592 593 Vector<SignerInfo> intResult = new Vector<SignerInfo>(); 594 for (int i = 0; i < signerInfos.length; i++) { 595 596 SignerInfo signerInfo = verify(signerInfos[i], bytes); 597 if (signerInfo != null) { 598 intResult.addElement(signerInfo); 599 } 600 } 601 if (!intResult.isEmpty()) { 602 603 SignerInfo[] result = new SignerInfo[intResult.size()]; 604 intResult.copyInto(result); 605 return result; 606 } 607 return null; 608 } 609 610 /** 611 * Returns all signerInfos which self-verify. 612 * 613 * @exception NoSuchAlgorithmException on unrecognized algorithms. 614 * @exception SignatureException on signature handling errors. 615 */ 616 public SignerInfo[] verify() 617 throws NoSuchAlgorithmException, SignatureException { 618 return verify(null); 619 } 620 621 /** 622 * Returns the version number of this PKCS7 block. 623 * @return the version or null if version is not specified 624 * for the content type. 625 */ 626 public BigInteger getVersion() { 627 return version; 628 } 629 630 /** 631 * Returns the message digest algorithms specified in this PKCS7 block. 632 * @return the array of Digest Algorithms or null if none are specified 633 * for the content type. 634 */ 635 public AlgorithmId[] getDigestAlgorithmIds() { 636 return digestAlgorithmIds; 637 } 638 639 /** 640 * Returns the content information specified in this PKCS7 block. 641 */ 642 public ContentInfo getContentInfo() { 643 return contentInfo; 644 } 645 646 /** 647 * Returns the X.509 certificates listed in this PKCS7 block. 648 * @return a clone of the array of X.509 certificates or null if 649 * none are specified for the content type. 650 */ 651 public X509Certificate[] getCertificates() { 652 if (certificates != null) 653 return certificates.clone(); 654 else 655 return null; 656 } 657 658 /** 659 * Returns the X.509 crls listed in this PKCS7 block. 660 * @return a clone of the array of X.509 crls or null if none 661 * are specified for the content type. 662 */ 663 public X509CRL[] getCRLs() { 664 if (crls != null) 665 return crls.clone(); 666 else 667 return null; 668 } 669 670 /** 671 * Returns the signer's information specified in this PKCS7 block. 672 * @return the array of Signer Infos or null if none are specified 673 * for the content type. 674 */ 675 public SignerInfo[] getSignerInfos() { 676 return signerInfos; 677 } 678 679 /** 680 * Returns the X.509 certificate listed in this PKCS7 block 681 * which has a matching serial number and Issuer name, or 682 * null if one is not found. 683 * 684 * @param serial the serial number of the certificate to retrieve. 685 * @param issuerName the Distinguished Name of the Issuer. 686 */ 687 public X509Certificate getCertificate(BigInteger serial, X500Name issuerName) { 688 if (certificates != null) { 689 if (certIssuerNames == null) 690 populateCertIssuerNames(); 691 for (int i = 0; i < certificates.length; i++) { 692 X509Certificate cert = certificates[i]; 693 BigInteger thisSerial = cert.getSerialNumber(); 694 if (serial.equals(thisSerial) 695 && issuerName.equals(certIssuerNames[i])) 696 { 697 return cert; 698 } 699 } 700 } 701 return null; 702 } 703 704 /** 705 * Populate array of Issuer DNs from certificates and convert 706 * each Principal to type X500Name if necessary. 707 */ 708 private void populateCertIssuerNames() { 709 if (certificates == null) 710 return; 711 712 certIssuerNames = new Principal[certificates.length]; 713 for (int i = 0; i < certificates.length; i++) { 714 X509Certificate cert = certificates[i]; 715 Principal certIssuerName = cert.getIssuerDN(); 716 if (!(certIssuerName instanceof X500Name)) { 717 // must extract the original encoded form of DN for 718 // subsequent name comparison checks (converting to a 719 // String and back to an encoded DN could cause the 720 // types of String attribute values to be changed) 721 try { 722 X509CertInfo tbsCert = 723 new X509CertInfo(cert.getTBSCertificate()); 724 certIssuerName = (Principal) 725 tbsCert.get(X509CertInfo.ISSUER + "." + 726 X509CertInfo.DN_NAME); 727 } catch (Exception e) { 728 // error generating X500Name object from the cert's 729 // issuer DN, leave name as is. 730 } 731 } 732 certIssuerNames[i] = certIssuerName; 733 } 734 } 735 736 /** 737 * Returns the PKCS7 block in a printable string form. 738 */ 739 public String toString() { 740 String out = ""; 741 742 out += contentInfo + "\n"; 743 if (version != null) 744 out += "PKCS7 :: version: " + Debug.toHexString(version) + "\n"; 745 if (digestAlgorithmIds != null) { 746 out += "PKCS7 :: digest AlgorithmIds: \n"; 747 for (int i = 0; i < digestAlgorithmIds.length; i++) 748 out += "\t" + digestAlgorithmIds[i] + "\n"; 749 } 750 if (certificates != null) { 751 out += "PKCS7 :: certificates: \n"; 752 for (int i = 0; i < certificates.length; i++) 753 out += "\t" + i + ". " + certificates[i] + "\n"; 754 } 755 if (crls != null) { 756 out += "PKCS7 :: crls: \n"; 757 for (int i = 0; i < crls.length; i++) 758 out += "\t" + i + ". " + crls[i] + "\n"; 759 } 760 if (signerInfos != null) { 761 out += "PKCS7 :: signer infos: \n"; 762 for (int i = 0; i < signerInfos.length; i++) 763 out += ("\t" + i + ". " + signerInfos[i] + "\n"); 764 } 765 return out; 766 } 767 768 /** 769 * Returns true if this is a JDK1.1.x-style PKCS#7 block, and false 770 * otherwise. 771 */ 772 public boolean isOldStyle() { 773 return this.oldStyle; 774 } 775 776 /** 777 * Assembles a PKCS #7 signed data message that optionally includes a 778 * signature timestamp. 779 * 780 * @param signature the signature bytes 781 * @param signerChain the signer's X.509 certificate chain 782 * @param content the content that is signed; specify null to not include 783 * it in the PKCS7 data 784 * @param signatureAlgorithm the name of the signature algorithm 785 * @param tsaURI the URI of the Timestamping Authority; or null if no 786 * timestamp is requested 787 * @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a 788 * numerical object identifier; or null if we leave the TSA server 789 * to choose one. This argument is only used when tsaURI is provided 790 * @return the bytes of the encoded PKCS #7 signed data message 791 * @throws NoSuchAlgorithmException The exception is thrown if the signature 792 * algorithm is unrecognised. 793 * @throws CertificateException The exception is thrown if an error occurs 794 * while processing the signer's certificate or the TSA's 795 * certificate. 796 * @throws IOException The exception is thrown if an error occurs while 797 * generating the signature timestamp or while generating the signed 798 * data message. 799 */ 800 public static byte[] generateSignedData(byte[] signature, 801 X509Certificate[] signerChain, 802 byte[] content, 803 String signatureAlgorithm, 804 URI tsaURI, 805 String tSAPolicyID, 806 String tSADigestAlg) 807 throws CertificateException, IOException, NoSuchAlgorithmException 808 { 809 810 // Generate the timestamp token 811 PKCS9Attributes unauthAttrs = null; 812 if (tsaURI != null) { 813 // Timestamp the signature 814 HttpTimestamper tsa = new HttpTimestamper(tsaURI); 815 byte[] tsToken = generateTimestampToken( 816 tsa, tSAPolicyID, tSADigestAlg, signature); 817 818 // Insert the timestamp token into the PKCS #7 signer info element 819 // (as an unsigned attribute) 820 unauthAttrs = 821 new PKCS9Attributes(new PKCS9Attribute[]{ 822 new PKCS9Attribute( 823 PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_STR, 824 tsToken)}); 825 } 826 827 // Create the SignerInfo 828 X500Name issuerName = 829 X500Name.asX500Name(signerChain[0].getIssuerX500Principal()); 830 BigInteger serialNumber = signerChain[0].getSerialNumber(); 831 String encAlg = AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm); 832 String digAlg = AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm); 833 SignerInfo signerInfo = new SignerInfo(issuerName, serialNumber, 834 AlgorithmId.get(digAlg), null, 835 AlgorithmId.get(encAlg), 836 signature, unauthAttrs); 837 838 // Create the PKCS #7 signed data message 839 SignerInfo[] signerInfos = {signerInfo}; 840 AlgorithmId[] algorithms = {signerInfo.getDigestAlgorithmId()}; 841 // Include or exclude content 842 ContentInfo contentInfo = (content == null) 843 ? new ContentInfo(ContentInfo.DATA_OID, null) 844 : new ContentInfo(content); 845 PKCS7 pkcs7 = new PKCS7(algorithms, contentInfo, 846 signerChain, signerInfos); 847 ByteArrayOutputStream p7out = new ByteArrayOutputStream(); 848 pkcs7.encodeSignedData(p7out); 849 850 return p7out.toByteArray(); 851 } 852 853 /** 854 * Requests, processes and validates a timestamp token from a TSA using 855 * common defaults. Uses the following defaults in the timestamp request: 856 * SHA-1 for the hash algorithm, a 64-bit nonce, and request certificate 857 * set to true. 858 * 859 * @param tsa the timestamping authority to use 860 * @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a 861 * numerical object identifier; or null if we leave the TSA server 862 * to choose one 863 * @param toBeTimestamped the token that is to be timestamped 864 * @return the encoded timestamp token 865 * @throws IOException The exception is thrown if an error occurs while 866 * communicating with the TSA, or a non-null 867 * TSAPolicyID is specified in the request but it 868 * does not match the one in the reply 869 * @throws CertificateException The exception is thrown if the TSA's 870 * certificate is not permitted for timestamping. 871 */ 872 private static byte[] generateTimestampToken(Timestamper tsa, 873 String tSAPolicyID, 874 String tSADigestAlg, 875 byte[] toBeTimestamped) 876 throws IOException, CertificateException 877 { 878 // Generate a timestamp 879 MessageDigest messageDigest = null; 880 TSRequest tsQuery = null; 881 try { 882 messageDigest = MessageDigest.getInstance(tSADigestAlg); 883 tsQuery = new TSRequest(tSAPolicyID, toBeTimestamped, messageDigest); 884 } catch (NoSuchAlgorithmException e) { 885 throw new IllegalArgumentException(e); 886 } 887 888 // Generate a nonce 889 BigInteger nonce = null; 890 if (SecureRandomHolder.RANDOM != null) { 891 nonce = new BigInteger(64, SecureRandomHolder.RANDOM); 892 tsQuery.setNonce(nonce); 893 } 894 tsQuery.requestCertificate(true); 895 896 TSResponse tsReply = tsa.generateTimestamp(tsQuery); 897 int status = tsReply.getStatusCode(); 898 // Handle TSP error 899 if (status != 0 && status != 1) { 900 throw new IOException("Error generating timestamp: " + 901 tsReply.getStatusCodeAsText() + " " + 902 tsReply.getFailureCodeAsText()); 903 } 904 905 if (tSAPolicyID != null && 906 !tSAPolicyID.equals(tsReply.getTimestampToken().getPolicyID())) { 907 throw new IOException("TSAPolicyID changed in " 908 + "timestamp token"); 909 } 910 PKCS7 tsToken = tsReply.getToken(); 911 912 TimestampToken tst = tsReply.getTimestampToken(); 913 try { 914 if (!tst.getHashAlgorithm().equals(AlgorithmId.get(tSADigestAlg))) { 915 throw new IOException("Digest algorithm not " + tSADigestAlg + " in " 916 + "timestamp token"); 917 } 918 } catch (NoSuchAlgorithmException nase) { 919 throw new IllegalArgumentException(); // should have been caught before 920 } 921 if (!MessageDigest.isEqual(tst.getHashedMessage(), 922 tsQuery.getHashedMessage())) { 923 throw new IOException("Digest octets changed in timestamp token"); 924 } 925 926 BigInteger replyNonce = tst.getNonce(); 927 if (replyNonce == null && nonce != null) { 928 throw new IOException("Nonce missing in timestamp token"); 929 } 930 if (replyNonce != null && !replyNonce.equals(nonce)) { 931 throw new IOException("Nonce changed in timestamp token"); 932 } 933 934 // Examine the TSA's certificate (if present) 935 for (SignerInfo si: tsToken.getSignerInfos()) { 936 X509Certificate cert = si.getCertificate(tsToken); 937 if (cert == null) { 938 // Error, we've already set tsRequestCertificate = true 939 throw new CertificateException( 940 "Certificate not included in timestamp token"); 941 } else { 942 if (!cert.getCriticalExtensionOIDs().contains( 943 EXTENDED_KEY_USAGE_OID)) { 944 throw new CertificateException( 945 "Certificate is not valid for timestamping"); 946 } 947 List<String> keyPurposes = cert.getExtendedKeyUsage(); 948 if (keyPurposes == null || 949 !keyPurposes.contains(KP_TIMESTAMPING_OID)) { 950 throw new CertificateException( 951 "Certificate is not valid for timestamping"); 952 } 953 } 954 } 955 return tsReply.getEncodedToken(); 956 } 957 }