1 package org.bouncycastle.cms; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 import java.security.NoSuchAlgorithmException; 7 import java.security.NoSuchProviderException; 8 import java.security.Provider; 9 import java.security.cert.CertStore; 10 import java.security.cert.CertStoreException; 11 import java.util.ArrayList; 12 import java.util.Enumeration; 13 import java.util.Iterator; 14 import java.util.List; 15 import java.util.Map; 16 17 import org.bouncycastle.asn1.ASN1Encodable; 18 import org.bouncycastle.asn1.ASN1EncodableVector; 19 import org.bouncycastle.asn1.ASN1InputStream; 20 import org.bouncycastle.asn1.ASN1ObjectIdentifier; 21 import org.bouncycastle.asn1.ASN1OctetString; 22 import org.bouncycastle.asn1.ASN1Primitive; 23 import org.bouncycastle.asn1.ASN1Sequence; 24 import org.bouncycastle.asn1.ASN1Set; 25 import org.bouncycastle.asn1.ASN1TaggedObject; 26 import org.bouncycastle.asn1.BERSequence; 27 import org.bouncycastle.asn1.DERSet; 28 import org.bouncycastle.asn1.cms.ContentInfo; 29 import org.bouncycastle.asn1.cms.SignedData; 30 import org.bouncycastle.asn1.cms.SignerInfo; 31 import org.bouncycastle.asn1.x509.AttributeCertificate; 32 import org.bouncycastle.asn1.x509.Certificate; 33 import org.bouncycastle.asn1.x509.CertificateList; 34 import org.bouncycastle.cert.X509AttributeCertificateHolder; 35 import org.bouncycastle.cert.X509CRLHolder; 36 import org.bouncycastle.cert.X509CertificateHolder; 37 import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; 38 import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder; 39 import org.bouncycastle.util.CollectionStore; 40 import org.bouncycastle.util.Store; 41 import org.bouncycastle.x509.NoSuchStoreException; 42 import org.bouncycastle.x509.X509Store; 43 44 /** 45 * general class for handling a pkcs7-signature message. 46 * 47 * A simple example of usage - note, in the example below the validity of 48 * the certificate isn't verified, just the fact that one of the certs 49 * matches the given signer... 50 * 51 * <pre> 52 * Store certStore = s.getCertificates(); 53 * SignerInformationStore signers = s.getSignerInfos(); 54 * Collection c = signers.getSigners(); 55 * Iterator it = c.iterator(); 56 * 57 * while (it.hasNext()) 58 * { 59 * SignerInformation signer = (SignerInformation)it.next(); 60 * Collection certCollection = certStore.getMatches(signer.getSID()); 61 * 62 * Iterator certIt = certCollection.iterator(); 63 * X509CertificateHolder cert = (X509CertificateHolder)certIt.next(); 64 * 65 * if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) 66 * { 67 * verified++; 68 * } 69 * } 70 * </pre> 71 */ 72 public class CMSSignedData 73 { 74 private static final CMSSignedHelper HELPER = CMSSignedHelper.INSTANCE; 75 76 SignedData signedData; 77 ContentInfo contentInfo; 78 CMSTypedData signedContent; 79 SignerInformationStore signerInfoStore; 80 X509Store attributeStore; 81 X509Store certificateStore; 82 X509Store crlStore; 83 private Map hashes; 84 85 private CMSSignedData( 86 CMSSignedData c) 87 { 88 this.signedData = c.signedData; 89 this.contentInfo = c.contentInfo; 90 this.signedContent = c.signedContent; 91 this.signerInfoStore = c.signerInfoStore; 92 } 93 94 public CMSSignedData( 95 byte[] sigBlock) 96 throws CMSException 97 { 98 this(CMSUtils.readContentInfo(sigBlock)); 99 } 100 101 public CMSSignedData( 102 CMSProcessable signedContent, 103 byte[] sigBlock) 104 throws CMSException 105 { 106 this(signedContent, CMSUtils.readContentInfo(sigBlock)); 107 } 108 109 /** 110 * Content with detached signature, digests precomputed 111 * 112 * @param hashes a map of precomputed digests for content indexed by name of hash. 113 * @param sigBlock the signature object. 114 */ 115 public CMSSignedData( 116 Map hashes, 117 byte[] sigBlock) 118 throws CMSException 119 { 120 this(hashes, CMSUtils.readContentInfo(sigBlock)); 121 } 122 123 /** 124 * base constructor - content with detached signature. 125 * 126 * @param signedContent the content that was signed. 127 * @param sigData the signature object. 128 */ 129 public CMSSignedData( 130 CMSProcessable signedContent, 131 InputStream sigData) 132 throws CMSException 133 { 134 this(signedContent, CMSUtils.readContentInfo(new ASN1InputStream(sigData))); 135 } 136 137 /** 138 * base constructor - with encapsulated content 139 */ 140 public CMSSignedData( 141 InputStream sigData) 142 throws CMSException 143 { 144 this(CMSUtils.readContentInfo(sigData)); 145 } 146 147 public CMSSignedData( 148 final CMSProcessable signedContent, 149 ContentInfo sigData) 150 throws CMSException 151 { 152 if (signedContent instanceof CMSTypedData) 153 { 154 this.signedContent = (CMSTypedData)signedContent; 155 } 156 else 157 { 158 this.signedContent = new CMSTypedData() 159 { 160 public ASN1ObjectIdentifier getContentType() 161 { 162 return signedData.getEncapContentInfo().getContentType(); 163 } 164 165 public void write(OutputStream out) 166 throws IOException, CMSException 167 { 168 signedContent.write(out); 169 } 170 171 public Object getContent() 172 { 173 return signedContent.getContent(); 174 } 175 }; 176 } 177 178 this.contentInfo = sigData; 179 this.signedData = getSignedData(); 180 } 181 182 public CMSSignedData( 183 Map hashes, 184 ContentInfo sigData) 185 throws CMSException 186 { 187 this.hashes = hashes; 188 this.contentInfo = sigData; 189 this.signedData = getSignedData(); 190 } 191 192 public CMSSignedData( 193 ContentInfo sigData) 194 throws CMSException 195 { 196 this.contentInfo = sigData; 197 this.signedData = getSignedData(); 198 199 // 200 // this can happen if the signed message is sent simply to send a 201 // certificate chain. 202 // 203 if (signedData.getEncapContentInfo().getContent() != null) 204 { 205 this.signedContent = new CMSProcessableByteArray(signedData.getEncapContentInfo().getContentType(), 206 ((ASN1OctetString)(signedData.getEncapContentInfo() 207 .getContent())).getOctets()); 208 } 209 else 210 { 211 this.signedContent = null; 212 } 213 } 214 215 private SignedData getSignedData() 216 throws CMSException 217 { 218 try 219 { 220 return SignedData.getInstance(contentInfo.getContent()); 221 } 222 catch (ClassCastException e) 223 { 224 throw new CMSException("Malformed content.", e); 225 } 226 catch (IllegalArgumentException e) 227 { 228 throw new CMSException("Malformed content.", e); 229 } 230 } 231 232 /** 233 * Return the version number for this object 234 */ 235 public int getVersion() 236 { 237 return signedData.getVersion().getValue().intValue(); 238 } 239 240 /** 241 * return the collection of signers that are associated with the 242 * signatures for the message. 243 */ 244 public SignerInformationStore getSignerInfos() 245 { 246 if (signerInfoStore == null) 247 { 248 ASN1Set s = signedData.getSignerInfos(); 249 List signerInfos = new ArrayList(); 250 SignatureAlgorithmIdentifierFinder sigAlgFinder = new DefaultSignatureAlgorithmIdentifierFinder(); 251 252 for (int i = 0; i != s.size(); i++) 253 { 254 SignerInfo info = SignerInfo.getInstance(s.getObjectAt(i)); 255 ASN1ObjectIdentifier contentType = signedData.getEncapContentInfo().getContentType(); 256 257 if (hashes == null) 258 { 259 signerInfos.add(new SignerInformation(info, contentType, signedContent, null)); 260 } 261 else 262 { 263 Object obj = hashes.keySet().iterator().next(); 264 byte[] hash = (obj instanceof String) ? (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm().getId()) : (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm()); 265 266 signerInfos.add(new SignerInformation(info, contentType, null, hash)); 267 } 268 } 269 270 signerInfoStore = new SignerInformationStore(signerInfos); 271 } 272 273 return signerInfoStore; 274 } 275 276 /** 277 * return a X509Store containing the attribute certificates, if any, contained 278 * in this message. 279 * 280 * @param type type of store to create 281 * @param provider name of provider to use 282 * @return a store of attribute certificates 283 * @exception NoSuchProviderException if the provider requested isn't available. 284 * @exception NoSuchStoreException if the store type isn't available. 285 * @exception CMSException if a general exception prevents creation of the X509Store 286 * @deprecated use base Store returning method 287 */ 288 public X509Store getAttributeCertificates( 289 String type, 290 String provider) 291 throws NoSuchStoreException, NoSuchProviderException, CMSException 292 { 293 return getAttributeCertificates(type, CMSUtils.getProvider(provider)); 294 } 295 296 /** 297 * return a X509Store containing the attribute certificates, if any, contained 298 * in this message. 299 * 300 * @param type type of store to create 301 * @param provider provider to use 302 * @return a store of attribute certificates 303 * @exception NoSuchStoreException if the store type isn't available. 304 * @exception CMSException if a general exception prevents creation of the X509Store 305 * @deprecated use base Store returning method 306 */ 307 public X509Store getAttributeCertificates( 308 String type, 309 Provider provider) 310 throws NoSuchStoreException, CMSException 311 { 312 if (attributeStore == null) 313 { 314 attributeStore = HELPER.createAttributeStore(type, provider, signedData.getCertificates()); 315 } 316 317 return attributeStore; 318 } 319 320 /** 321 * return a X509Store containing the public key certificates, if any, contained 322 * in this message. 323 * 324 * @param type type of store to create 325 * @param provider name of provider to use 326 * @return a store of public key certificates 327 * @exception NoSuchProviderException if the provider requested isn't available. 328 * @exception NoSuchStoreException if the store type isn't available. 329 * @exception CMSException if a general exception prevents creation of the X509Store 330 * @deprecated use base Store returning method 331 */ 332 public X509Store getCertificates( 333 String type, 334 String provider) 335 throws NoSuchStoreException, NoSuchProviderException, CMSException 336 { 337 return getCertificates(type, CMSUtils.getProvider(provider)); 338 } 339 340 /** 341 * return a X509Store containing the public key certificates, if any, contained 342 * in this message. 343 * 344 * @param type type of store to create 345 * @param provider provider to use 346 * @return a store of public key certificates 347 * @exception NoSuchStoreException if the store type isn't available. 348 * @exception CMSException if a general exception prevents creation of the X509Store 349 * @deprecated use base Store returning method 350 */ 351 public X509Store getCertificates( 352 String type, 353 Provider provider) 354 throws NoSuchStoreException, CMSException 355 { 356 if (certificateStore == null) 357 { 358 certificateStore = HELPER.createCertificateStore(type, provider, signedData.getCertificates()); 359 } 360 361 return certificateStore; 362 } 363 364 /** 365 * return a X509Store containing CRLs, if any, contained 366 * in this message. 367 * 368 * @param type type of store to create 369 * @param provider name of provider to use 370 * @return a store of CRLs 371 * @exception NoSuchProviderException if the provider requested isn't available. 372 * @exception NoSuchStoreException if the store type isn't available. 373 * @exception CMSException if a general exception prevents creation of the X509Store 374 * @deprecated use base Store returning method 375 */ 376 public X509Store getCRLs( 377 String type, 378 String provider) 379 throws NoSuchStoreException, NoSuchProviderException, CMSException 380 { 381 return getCRLs(type, CMSUtils.getProvider(provider)); 382 } 383 384 /** 385 * return a X509Store containing CRLs, if any, contained 386 * in this message. 387 * 388 * @param type type of store to create 389 * @param provider provider to use 390 * @return a store of CRLs 391 * @exception NoSuchStoreException if the store type isn't available. 392 * @exception CMSException if a general exception prevents creation of the X509Store 393 * @deprecated use base Store returning method 394 */ 395 public X509Store getCRLs( 396 String type, 397 Provider provider) 398 throws NoSuchStoreException, CMSException 399 { 400 if (crlStore == null) 401 { 402 crlStore = HELPER.createCRLsStore(type, provider, signedData.getCRLs()); 403 } 404 405 return crlStore; 406 } 407 408 /** 409 * return a CertStore containing the certificates and CRLs associated with 410 * this message. 411 * 412 * @exception NoSuchProviderException if the provider requested isn't available. 413 * @exception NoSuchAlgorithmException if the cert store isn't available. 414 * @exception CMSException if a general exception prevents creation of the CertStore 415 * @deprecated use base Store returning method 416 */ 417 public CertStore getCertificatesAndCRLs( 418 String type, 419 String provider) 420 throws NoSuchAlgorithmException, NoSuchProviderException, CMSException 421 { 422 return getCertificatesAndCRLs(type, CMSUtils.getProvider(provider)); 423 } 424 425 /** 426 * return a CertStore containing the certificates and CRLs associated with 427 * this message. 428 * 429 * @exception NoSuchAlgorithmException if the cert store isn't available. 430 * @exception CMSException if a general exception prevents creation of the CertStore 431 * @deprecated use base Store returning method 432 */ 433 public CertStore getCertificatesAndCRLs( 434 String type, 435 Provider provider) 436 throws NoSuchAlgorithmException, CMSException 437 { 438 ASN1Set certSet = signedData.getCertificates(); 439 ASN1Set crlSet = signedData.getCRLs(); 440 441 return HELPER.createCertStore(type, provider, certSet, crlSet); 442 } 443 444 public Store getCertificates() 445 { 446 ASN1Set certSet = signedData.getCertificates(); 447 448 if (certSet != null) 449 { 450 List certList = new ArrayList(certSet.size()); 451 452 for (Enumeration en = certSet.getObjects(); en.hasMoreElements();) 453 { 454 ASN1Primitive obj = ((ASN1Encodable)en.nextElement()).toASN1Primitive(); 455 456 if (obj instanceof ASN1Sequence) 457 { 458 certList.add(new X509CertificateHolder(Certificate.getInstance(obj))); 459 } 460 } 461 462 return new CollectionStore(certList); 463 } 464 465 return new CollectionStore(new ArrayList()); 466 } 467 468 public Store getCRLs() 469 { 470 ASN1Set crlSet = signedData.getCRLs(); 471 472 if (crlSet != null) 473 { 474 List crlList = new ArrayList(crlSet.size()); 475 476 for (Enumeration en = crlSet.getObjects(); en.hasMoreElements();) 477 { 478 ASN1Primitive obj = ((ASN1Encodable)en.nextElement()).toASN1Primitive(); 479 480 if (obj instanceof ASN1Sequence) 481 { 482 crlList.add(new X509CRLHolder(CertificateList.getInstance(obj))); 483 } 484 } 485 486 return new CollectionStore(crlList); 487 } 488 489 return new CollectionStore(new ArrayList()); 490 } 491 492 public Store getAttributeCertificates() 493 { 494 ASN1Set certSet = signedData.getCertificates(); 495 496 if (certSet != null) 497 { 498 List certList = new ArrayList(certSet.size()); 499 500 for (Enumeration en = certSet.getObjects(); en.hasMoreElements();) 501 { 502 ASN1Primitive obj = ((ASN1Encodable)en.nextElement()).toASN1Primitive(); 503 504 if (obj instanceof ASN1TaggedObject) 505 { 506 certList.add(new X509AttributeCertificateHolder(AttributeCertificate.getInstance(((ASN1TaggedObject)obj).getObject()))); 507 } 508 } 509 510 return new CollectionStore(certList); 511 } 512 513 return new CollectionStore(new ArrayList()); 514 } 515 516 /** 517 * Return the a string representation of the OID associated with the 518 * encapsulated content info structure carried in the signed data. 519 * 520 * @return the OID for the content type. 521 */ 522 public String getSignedContentTypeOID() 523 { 524 return signedData.getEncapContentInfo().getContentType().getId(); 525 } 526 527 public CMSTypedData getSignedContent() 528 { 529 return signedContent; 530 } 531 532 /** 533 * return the ContentInfo 534 * @deprecated use toASN1Structure() 535 */ 536 public ContentInfo getContentInfo() 537 { 538 return contentInfo; 539 } 540 541 /** 542 * return the ContentInfo 543 */ 544 public ContentInfo toASN1Structure() 545 { 546 return contentInfo; 547 } 548 549 /** 550 * return the ASN.1 encoded representation of this object. 551 */ 552 public byte[] getEncoded() 553 throws IOException 554 { 555 return contentInfo.getEncoded(); 556 } 557 558 /** 559 * Replace the signerinformation store associated with this 560 * CMSSignedData object with the new one passed in. You would 561 * probably only want to do this if you wanted to change the unsigned 562 * attributes associated with a signer, or perhaps delete one. 563 * 564 * @param signedData the signed data object to be used as a base. 565 * @param signerInformationStore the new signer information store to use. 566 * @return a new signed data object. 567 */ 568 public static CMSSignedData replaceSigners( 569 CMSSignedData signedData, 570 SignerInformationStore signerInformationStore) 571 { 572 // 573 // copy 574 // 575 CMSSignedData cms = new CMSSignedData(signedData); 576 577 // 578 // replace the store 579 // 580 cms.signerInfoStore = signerInformationStore; 581 582 // 583 // replace the signers in the SignedData object 584 // 585 ASN1EncodableVector digestAlgs = new ASN1EncodableVector(); 586 ASN1EncodableVector vec = new ASN1EncodableVector(); 587 588 Iterator it = signerInformationStore.getSigners().iterator(); 589 while (it.hasNext()) 590 { 591 SignerInformation signer = (SignerInformation)it.next(); 592 digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID())); 593 vec.add(signer.toASN1Structure()); 594 } 595 596 ASN1Set digests = new DERSet(digestAlgs); 597 ASN1Set signers = new DERSet(vec); 598 ASN1Sequence sD = (ASN1Sequence)signedData.signedData.toASN1Primitive(); 599 600 vec = new ASN1EncodableVector(); 601 602 // 603 // signers are the last item in the sequence. 604 // 605 vec.add(sD.getObjectAt(0)); // version 606 vec.add(digests); 607 608 for (int i = 2; i != sD.size() - 1; i++) 609 { 610 vec.add(sD.getObjectAt(i)); 611 } 612 613 vec.add(signers); 614 615 cms.signedData = SignedData.getInstance(new BERSequence(vec)); 616 617 // 618 // replace the contentInfo with the new one 619 // 620 cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData); 621 622 return cms; 623 } 624 625 /** 626 * Replace the certificate and CRL information associated with this 627 * CMSSignedData object with the new one passed in. 628 * 629 * @param signedData the signed data object to be used as a base. 630 * @param certsAndCrls the new certificates and CRLs to be used. 631 * @return a new signed data object. 632 * @exception CMSException if there is an error processing the CertStore 633 * @deprecated use method taking Store arguments. 634 */ 635 public static CMSSignedData replaceCertificatesAndCRLs( 636 CMSSignedData signedData, 637 CertStore certsAndCrls) 638 throws CMSException 639 { 640 // 641 // copy 642 // 643 CMSSignedData cms = new CMSSignedData(signedData); 644 645 // 646 // replace the certs and crls in the SignedData object 647 // 648 ASN1Set certs = null; 649 ASN1Set crls = null; 650 651 try 652 { 653 ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils.getCertificatesFromStore(certsAndCrls)); 654 655 if (set.size() != 0) 656 { 657 certs = set; 658 } 659 } 660 catch (CertStoreException e) 661 { 662 throw new CMSException("error getting certs from certStore", e); 663 } 664 665 try 666 { 667 ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(certsAndCrls)); 668 669 if (set.size() != 0) 670 { 671 crls = set; 672 } 673 } 674 catch (CertStoreException e) 675 { 676 throw new CMSException("error getting crls from certStore", e); 677 } 678 679 // 680 // replace the CMS structure. 681 // 682 cms.signedData = new SignedData(signedData.signedData.getDigestAlgorithms(), 683 signedData.signedData.getEncapContentInfo(), 684 certs, 685 crls, 686 signedData.signedData.getSignerInfos()); 687 688 // 689 // replace the contentInfo with the new one 690 // 691 cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData); 692 693 return cms; 694 } 695 696 /** 697 * Replace the certificate and CRL information associated with this 698 * CMSSignedData object with the new one passed in. 699 * 700 * @param signedData the signed data object to be used as a base. 701 * @param certificates the new certificates to be used. 702 * @param attrCerts the new attribute certificates to be used. 703 * @param crls the new CRLs to be used. 704 * @return a new signed data object. 705 * @exception CMSException if there is an error processing the CertStore 706 */ 707 public static CMSSignedData replaceCertificatesAndCRLs( 708 CMSSignedData signedData, 709 Store certificates, 710 Store attrCerts, 711 Store crls) 712 throws CMSException 713 { 714 // 715 // copy 716 // 717 CMSSignedData cms = new CMSSignedData(signedData); 718 719 // 720 // replace the certs and crls in the SignedData object 721 // 722 ASN1Set certSet = null; 723 ASN1Set crlSet = null; 724 725 if (certificates != null || attrCerts != null) 726 { 727 List certs = new ArrayList(); 728 729 if (certificates != null) 730 { 731 certs.addAll(CMSUtils.getCertificatesFromStore(certificates)); 732 } 733 if (attrCerts != null) 734 { 735 certs.addAll(CMSUtils.getAttributeCertificatesFromStore(attrCerts)); 736 } 737 738 ASN1Set set = CMSUtils.createBerSetFromList(certs); 739 740 if (set.size() != 0) 741 { 742 certSet = set; 743 } 744 } 745 746 if (crls != null) 747 { 748 ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(crls)); 749 750 if (set.size() != 0) 751 { 752 crlSet = set; 753 } 754 } 755 756 // 757 // replace the CMS structure. 758 // 759 cms.signedData = new SignedData(signedData.signedData.getDigestAlgorithms(), 760 signedData.signedData.getEncapContentInfo(), 761 certSet, 762 crlSet, 763 signedData.signedData.getSignerInfos()); 764 765 // 766 // replace the contentInfo with the new one 767 // 768 cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData); 769 770 return cms; 771 } 772 } 773