1 /* 2 * Copyright (c) 1996, 2006, 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.util.*; 31 import java.security.cert.Certificate; 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.util.*; 40 import sun.security.x509.AlgorithmId; 41 import sun.security.x509.CertificateIssuerName; 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 * Unmarshals a PKCS7 block from its encoded form, parsing the 74 * encoded bytes from the InputStream. 75 * 76 * @param in an input stream holding at least one PKCS7 block. 77 * @exception ParsingException on parsing errors. 78 * @exception IOException on other errors. 79 */ 80 public PKCS7(InputStream in) throws ParsingException, IOException { 81 DataInputStream dis = new DataInputStream(in); 82 byte[] data = new byte[dis.available()]; 83 dis.readFully(data); 84 85 parse(new DerInputStream(data)); 86 } 87 88 /** 89 * Unmarshals a PKCS7 block from its encoded form, parsing the 90 * encoded bytes from the DerInputStream. 91 * 92 * @param derin a DerInputStream holding at least one PKCS7 block. 93 * @exception ParsingException on parsing errors. 94 */ 95 public PKCS7(DerInputStream derin) throws ParsingException { 96 parse(derin); 97 } 98 99 /** 100 * Unmarshals a PKCS7 block from its encoded form, parsing the 101 * encoded bytes. 102 * 103 * @param bytes the encoded bytes. 104 * @exception ParsingException on parsing errors. 105 */ 106 public PKCS7(byte[] bytes) throws ParsingException { 107 try { 108 DerInputStream derin = new DerInputStream(bytes); 109 parse(derin); 110 } catch (IOException ioe1) { 111 ParsingException pe = new ParsingException( 112 "Unable to parse the encoded bytes"); 113 pe.initCause(ioe1); 114 throw pe; 115 } 116 } 117 118 /* 119 * Parses a PKCS#7 block. 120 */ 121 private void parse(DerInputStream derin) 122 throws ParsingException 123 { 124 try { 125 derin.mark(derin.available()); 126 // try new (i.e., JDK1.2) style 127 parse(derin, false); 128 } catch (IOException ioe) { 129 try { 130 derin.reset(); 131 // try old (i.e., JDK1.1.x) style 132 parse(derin, true); 133 oldStyle = true; 134 } catch (IOException ioe1) { 135 ParsingException pe = new ParsingException( 136 ioe1.getMessage()); 137 pe.initCause(ioe1); 138 throw pe; 139 } 140 } 141 } 142 143 /** 144 * Parses a PKCS#7 block. 145 * 146 * @param derin the ASN.1 encoding of the PKCS#7 block. 147 * @param oldStyle flag indicating whether or not the given PKCS#7 block 148 * is encoded according to JDK1.1.x. 149 */ 150 private void parse(DerInputStream derin, boolean oldStyle) 151 throws IOException 152 { 153 contentInfo = new ContentInfo(derin, oldStyle); 154 contentType = contentInfo.contentType; 155 DerValue content = contentInfo.getContent(); 156 157 if (contentType.equals(ContentInfo.SIGNED_DATA_OID)) { 158 parseSignedData(content); 159 } else if (contentType.equals(ContentInfo.OLD_SIGNED_DATA_OID)) { 160 // This is for backwards compatibility with JDK 1.1.x 161 parseOldSignedData(content); 162 } else if (contentType.equals(ContentInfo.NETSCAPE_CERT_SEQUENCE_OID)){ 163 parseNetscapeCertChain(content); 164 } else { 165 throw new ParsingException("content type " + contentType + 166 " not supported."); 167 } 168 } 169 170 /** 171 * Construct an initialized PKCS7 block. 172 * 173 * @param digestAlgorithmIds the message digest algorithm identifiers. 174 * @param contentInfo the content information. 175 * @param certificates an array of X.509 certificates. 176 * @param signerInfos an array of signer information. 177 */ 178 public PKCS7(AlgorithmId[] digestAlgorithmIds, 179 ContentInfo contentInfo, 180 X509Certificate[] certificates, 181 SignerInfo[] signerInfos) { 182 183 version = BigInteger.ONE; 184 this.digestAlgorithmIds = digestAlgorithmIds; 185 this.contentInfo = contentInfo; 186 this.certificates = certificates; 187 this.signerInfos = signerInfos; 188 } 189 190 private void parseNetscapeCertChain(DerValue val) 191 throws ParsingException, IOException { 192 DerInputStream dis = new DerInputStream(val.toByteArray()); 193 DerValue[] contents = dis.getSequence(2); 194 certificates = new X509Certificate[contents.length]; 195 196 CertificateFactory certfac = null; 197 try { 198 certfac = CertificateFactory.getInstance("X.509"); 199 } catch (CertificateException ce) { 200 // do nothing 201 } 202 203 for (int i=0; i < contents.length; i++) { 204 ByteArrayInputStream bais = null; 205 try { 206 if (certfac == null) 207 certificates[i] = new X509CertImpl(contents[i]); 208 else { 209 byte[] encoded = contents[i].toByteArray(); 210 bais = new ByteArrayInputStream(encoded); 211 certificates[i] = 212 (X509Certificate)certfac.generateCertificate(bais); 213 bais.close(); 214 bais = null; 215 } 216 } catch (CertificateException ce) { 217 ParsingException pe = new ParsingException(ce.getMessage()); 218 pe.initCause(ce); 219 throw pe; 220 } catch (IOException ioe) { 221 ParsingException pe = new ParsingException(ioe.getMessage()); 222 pe.initCause(ioe); 223 throw pe; 224 } finally { 225 if (bais != null) 226 bais.close(); 227 } 228 } 229 } 230 231 private void parseSignedData(DerValue val) 232 throws ParsingException, IOException { 233 234 DerInputStream dis = val.toDerInputStream(); 235 236 // Version 237 version = dis.getBigInteger(); 238 239 // digestAlgorithmIds 240 DerValue[] digestAlgorithmIdVals = dis.getSet(1); 241 int len = digestAlgorithmIdVals.length; 242 digestAlgorithmIds = new AlgorithmId[len]; 243 try { 244 for (int i = 0; i < len; i++) { 245 DerValue oid = digestAlgorithmIdVals[i]; 246 digestAlgorithmIds[i] = AlgorithmId.parse(oid); 247 } 248 249 } catch (IOException e) { 250 ParsingException pe = 251 new ParsingException("Error parsing digest AlgorithmId IDs: " + 252 e.getMessage()); 253 pe.initCause(e); 254 throw pe; 255 } 256 // contentInfo 257 contentInfo = new ContentInfo(dis); 258 259 CertificateFactory certfac = null; 260 try { 261 certfac = CertificateFactory.getInstance("X.509"); 262 } catch (CertificateException ce) { 263 // do nothing 264 } 265 266 /* 267 * check if certificates (implicit tag) are provided 268 * (certificates are OPTIONAL) 269 */ 270 if ((byte)(dis.peekByte()) == (byte)0xA0) { 271 DerValue[] certVals = dis.getSet(2, true); 272 273 len = certVals.length; 274 certificates = new X509Certificate[len]; 275 276 for (int i = 0; i < len; i++) { 277 ByteArrayInputStream bais = null; 278 try { 279 if (certfac == null) 280 certificates[i] = new X509CertImpl(certVals[i]); 281 else { 282 byte[] encoded = certVals[i].toByteArray(); 283 bais = new ByteArrayInputStream(encoded); 284 certificates[i] = 285 (X509Certificate)certfac.generateCertificate(bais); 286 bais.close(); 287 bais = null; 288 } 289 } catch (CertificateException ce) { 290 ParsingException pe = new ParsingException(ce.getMessage()); 291 pe.initCause(ce); 292 throw pe; 293 } catch (IOException ioe) { 294 ParsingException pe = new ParsingException(ioe.getMessage()); 295 pe.initCause(ioe); 296 throw pe; 297 } finally { 298 if (bais != null) 299 bais.close(); 300 } 301 } 302 } 303 304 // check if crls (implicit tag) are provided (crls are OPTIONAL) 305 if ((byte)(dis.peekByte()) == (byte)0xA1) { 306 DerValue[] crlVals = dis.getSet(1, true); 307 308 len = crlVals.length; 309 crls = new X509CRL[len]; 310 311 for (int i = 0; i < len; i++) { 312 ByteArrayInputStream bais = null; 313 try { 314 if (certfac == null) 315 crls[i] = (X509CRL) new X509CRLImpl(crlVals[i]); 316 else { 317 byte[] encoded = crlVals[i].toByteArray(); 318 bais = new ByteArrayInputStream(encoded); 319 crls[i] = (X509CRL) certfac.generateCRL(bais); 320 bais.close(); 321 bais = null; 322 } 323 } catch (CRLException e) { 324 ParsingException pe = 325 new ParsingException(e.getMessage()); 326 pe.initCause(e); 327 throw pe; 328 } finally { 329 if (bais != null) 330 bais.close(); 331 } 332 } 333 } 334 335 // signerInfos 336 DerValue[] signerInfoVals = dis.getSet(1); 337 338 len = signerInfoVals.length; 339 signerInfos = new SignerInfo[len]; 340 341 for (int i = 0; i < len; i++) { 342 DerInputStream in = signerInfoVals[i].toDerInputStream(); 343 signerInfos[i] = new SignerInfo(in); 344 } 345 } 346 347 /* 348 * Parses an old-style SignedData encoding (for backwards 349 * compatibility with JDK1.1.x). 350 */ 351 private void parseOldSignedData(DerValue val) 352 throws ParsingException, IOException 353 { 354 DerInputStream dis = val.toDerInputStream(); 355 356 // Version 357 version = dis.getBigInteger(); 358 359 // digestAlgorithmIds 360 DerValue[] digestAlgorithmIdVals = dis.getSet(1); 361 int len = digestAlgorithmIdVals.length; 362 363 digestAlgorithmIds = new AlgorithmId[len]; 364 try { 365 for (int i = 0; i < len; i++) { 366 DerValue oid = digestAlgorithmIdVals[i]; 367 digestAlgorithmIds[i] = AlgorithmId.parse(oid); 368 } 369 } catch (IOException e) { 370 throw new ParsingException("Error parsing digest AlgorithmId IDs"); 371 } 372 373 // contentInfo 374 contentInfo = new ContentInfo(dis, true); 375 376 // certificates 377 CertificateFactory certfac = null; 378 try { 379 certfac = CertificateFactory.getInstance("X.509"); 380 } catch (CertificateException ce) { 381 // do nothing 382 } 383 DerValue[] certVals = dis.getSet(2); 384 len = certVals.length; 385 certificates = new X509Certificate[len]; 386 387 for (int i = 0; i < len; i++) { 388 ByteArrayInputStream bais = null; 389 try { 390 if (certfac == null) 391 certificates[i] = new X509CertImpl(certVals[i]); 392 else { 393 byte[] encoded = certVals[i].toByteArray(); 394 bais = new ByteArrayInputStream(encoded); 395 certificates[i] = 396 (X509Certificate)certfac.generateCertificate(bais); 397 bais.close(); 398 bais = null; 399 } 400 } catch (CertificateException ce) { 401 ParsingException pe = new ParsingException(ce.getMessage()); 402 pe.initCause(ce); 403 throw pe; 404 } catch (IOException ioe) { 405 ParsingException pe = new ParsingException(ioe.getMessage()); 406 pe.initCause(ioe); 407 throw pe; 408 } finally { 409 if (bais != null) 410 bais.close(); 411 } 412 } 413 414 // crls are ignored. 415 dis.getSet(0); 416 417 // signerInfos 418 DerValue[] signerInfoVals = dis.getSet(1); 419 len = signerInfoVals.length; 420 signerInfos = new SignerInfo[len]; 421 for (int i = 0; i < len; i++) { 422 DerInputStream in = signerInfoVals[i].toDerInputStream(); 423 signerInfos[i] = new SignerInfo(in, true); 424 } 425 } 426 427 /** 428 * Encodes the signed data to an output stream. 429 * 430 * @param out the output stream to write the encoded data to. 431 * @exception IOException on encoding errors. 432 */ 433 public void encodeSignedData(OutputStream out) throws IOException { 434 DerOutputStream derout = new DerOutputStream(); 435 encodeSignedData(derout); 436 out.write(derout.toByteArray()); 437 } 438 439 /** 440 * Encodes the signed data to a DerOutputStream. 441 * 442 * @param out the DerOutputStream to write the encoded data to. 443 * @exception IOException on encoding errors. 444 */ 445 public void encodeSignedData(DerOutputStream out) 446 throws IOException 447 { 448 DerOutputStream signedData = new DerOutputStream(); 449 450 // version 451 signedData.putInteger(version); 452 453 // digestAlgorithmIds 454 signedData.putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds); 455 456 // contentInfo 457 contentInfo.encode(signedData); 458 459 // certificates (optional) 460 if (certificates != null && certificates.length != 0) { 461 // cast to X509CertImpl[] since X509CertImpl implements DerEncoder 462 X509CertImpl implCerts[] = new X509CertImpl[certificates.length]; 463 for (int i = 0; i < certificates.length; i++) { 464 if (certificates[i] instanceof X509CertImpl) 465 implCerts[i] = (X509CertImpl) certificates[i]; 466 else { 467 try { 468 byte[] encoded = certificates[i].getEncoded(); 469 implCerts[i] = new X509CertImpl(encoded); 470 } catch (CertificateException ce) { 471 IOException ie = new IOException(ce.getMessage()); 472 ie.initCause(ce); 473 throw ie; 474 } 475 } 476 } 477 478 // Add the certificate set (tagged with [0] IMPLICIT) 479 // to the signed data 480 signedData.putOrderedSetOf((byte)0xA0, implCerts); 481 } 482 483 // no crls (OPTIONAL field) 484 485 // signerInfos 486 signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos); 487 488 // making it a signed data block 489 DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence, 490 signedData.toByteArray()); 491 492 // making it a content info sequence 493 ContentInfo block = new ContentInfo(ContentInfo.SIGNED_DATA_OID, 494 signedDataSeq); 495 496 // writing out the contentInfo sequence 497 block.encode(out); 498 } 499 500 /** 501 * This verifies a given SignerInfo. 502 * 503 * @param info the signer information. 504 * @param bytes the DER encoded content information. 505 * 506 * @exception NoSuchAlgorithmException on unrecognized algorithms. 507 * @exception SignatureException on signature handling errors. 508 */ 509 public SignerInfo verify(SignerInfo info, byte[] bytes) 510 throws NoSuchAlgorithmException, SignatureException { 511 return info.verify(this, bytes); 512 } 513 514 /** 515 * Returns all signerInfos which self-verify. 516 * 517 * @param bytes the DER encoded content information. 518 * 519 * @exception NoSuchAlgorithmException on unrecognized algorithms. 520 * @exception SignatureException on signature handling errors. 521 */ 522 public SignerInfo[] verify(byte[] bytes) 523 throws NoSuchAlgorithmException, SignatureException { 524 525 Vector<SignerInfo> intResult = new Vector<SignerInfo>(); 526 for (int i = 0; i < signerInfos.length; i++) { 527 528 SignerInfo signerInfo = verify(signerInfos[i], bytes); 529 if (signerInfo != null) { 530 intResult.addElement(signerInfo); 531 } 532 } 533 if (intResult.size() != 0) { 534 535 SignerInfo[] result = new SignerInfo[intResult.size()]; 536 intResult.copyInto(result); 537 return result; 538 } 539 return null; 540 } 541 542 /** 543 * Returns all signerInfos which self-verify. 544 * 545 * @exception NoSuchAlgorithmException on unrecognized algorithms. 546 * @exception SignatureException on signature handling errors. 547 */ 548 public SignerInfo[] verify() 549 throws NoSuchAlgorithmException, SignatureException { 550 return verify(null); 551 } 552 553 /** 554 * Returns the version number of this PKCS7 block. 555 * @return the version or null if version is not specified 556 * for the content type. 557 */ 558 public BigInteger getVersion() { 559 return version; 560 } 561 562 /** 563 * Returns the message digest algorithms specified in this PKCS7 block. 564 * @return the array of Digest Algorithms or null if none are specified 565 * for the content type. 566 */ 567 public AlgorithmId[] getDigestAlgorithmIds() { 568 return digestAlgorithmIds; 569 } 570 571 /** 572 * Returns the content information specified in this PKCS7 block. 573 */ 574 public ContentInfo getContentInfo() { 575 return contentInfo; 576 } 577 578 /** 579 * Returns the X.509 certificates listed in this PKCS7 block. 580 * @return a clone of the array of X.509 certificates or null if 581 * none are specified for the content type. 582 */ 583 public X509Certificate[] getCertificates() { 584 if (certificates != null) 585 return certificates.clone(); 586 else 587 return null; 588 } 589 590 /** 591 * Returns the X.509 crls listed in this PKCS7 block. 592 * @return a clone of the array of X.509 crls or null if none 593 * are specified for the content type. 594 */ 595 public X509CRL[] getCRLs() { 596 if (crls != null) 597 return crls.clone(); 598 else 599 return null; 600 } 601 602 /** 603 * Returns the signer's information specified in this PKCS7 block. 604 * @return the array of Signer Infos or null if none are specified 605 * for the content type. 606 */ 607 public SignerInfo[] getSignerInfos() { 608 return signerInfos; 609 } 610 611 /** 612 * Returns the X.509 certificate listed in this PKCS7 block 613 * which has a matching serial number and Issuer name, or 614 * null if one is not found. 615 * 616 * @param serial the serial number of the certificate to retrieve. 617 * @param issuerName the Distinguished Name of the Issuer. 618 */ 619 public X509Certificate getCertificate(BigInteger serial, X500Name issuerName) { 620 if (certificates != null) { 621 if (certIssuerNames == null) 622 populateCertIssuerNames(); 623 for (int i = 0; i < certificates.length; i++) { 624 X509Certificate cert = certificates[i]; 625 BigInteger thisSerial = cert.getSerialNumber(); 626 if (serial.equals(thisSerial) 627 && issuerName.equals(certIssuerNames[i])) 628 { 629 return cert; 630 } 631 } 632 } 633 return null; 634 } 635 636 /** 637 * Populate array of Issuer DNs from certificates and convert 638 * each Principal to type X500Name if necessary. 639 */ 640 private void populateCertIssuerNames() { 641 if (certificates == null) 642 return; 643 644 certIssuerNames = new Principal[certificates.length]; 645 for (int i = 0; i < certificates.length; i++) { 646 X509Certificate cert = certificates[i]; 647 Principal certIssuerName = cert.getIssuerDN(); 648 if (!(certIssuerName instanceof X500Name)) { 649 // must extract the original encoded form of DN for 650 // subsequent name comparison checks (converting to a 651 // String and back to an encoded DN could cause the 652 // types of String attribute values to be changed) 653 try { 654 X509CertInfo tbsCert = 655 new X509CertInfo(cert.getTBSCertificate()); 656 certIssuerName = (Principal) 657 tbsCert.get(CertificateIssuerName.NAME + "." + 658 CertificateIssuerName.DN_NAME); 659 } catch (Exception e) { 660 // error generating X500Name object from the cert's 661 // issuer DN, leave name as is. 662 } 663 } 664 certIssuerNames[i] = certIssuerName; 665 } 666 } 667 668 /** 669 * Returns the PKCS7 block in a printable string form. 670 */ 671 public String toString() { 672 String out = ""; 673 674 out += contentInfo + "\n"; 675 if (version != null) 676 out += "PKCS7 :: version: " + Debug.toHexString(version) + "\n"; 677 if (digestAlgorithmIds != null) { 678 out += "PKCS7 :: digest AlgorithmIds: \n"; 679 for (int i = 0; i < digestAlgorithmIds.length; i++) 680 out += "\t" + digestAlgorithmIds[i] + "\n"; 681 } 682 if (certificates != null) { 683 out += "PKCS7 :: certificates: \n"; 684 for (int i = 0; i < certificates.length; i++) 685 out += "\t" + i + ". " + certificates[i] + "\n"; 686 } 687 if (crls != null) { 688 out += "PKCS7 :: crls: \n"; 689 for (int i = 0; i < crls.length; i++) 690 out += "\t" + i + ". " + crls[i] + "\n"; 691 } 692 if (signerInfos != null) { 693 out += "PKCS7 :: signer infos: \n"; 694 for (int i = 0; i < signerInfos.length; i++) 695 out += ("\t" + i + ". " + signerInfos[i] + "\n"); 696 } 697 return out; 698 } 699 700 /** 701 * Returns true if this is a JDK1.1.x-style PKCS#7 block, and false 702 * otherwise. 703 */ 704 public boolean isOldStyle() { 705 return this.oldStyle; 706 } 707 }