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