1package org.bouncycastle.cms; 2 3import java.io.IOException; 4import java.io.InputStream; 5import java.io.OutputStream; 6import java.util.ArrayList; 7import java.util.Collection; 8import java.util.Iterator; 9import java.util.List; 10import java.util.Map; 11 12import org.bouncycastle.asn1.ASN1EncodableVector; 13import org.bouncycastle.asn1.ASN1InputStream; 14import org.bouncycastle.asn1.ASN1ObjectIdentifier; 15import org.bouncycastle.asn1.ASN1OctetString; 16import org.bouncycastle.asn1.ASN1Sequence; 17import org.bouncycastle.asn1.ASN1Set; 18import org.bouncycastle.asn1.BERSequence; 19import org.bouncycastle.asn1.DERSet; 20import org.bouncycastle.asn1.cms.ContentInfo; 21import org.bouncycastle.asn1.cms.SignedData; 22import org.bouncycastle.asn1.cms.SignerInfo; 23import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; 24import org.bouncycastle.operator.OperatorCreationException; 25import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder; 26import org.bouncycastle.util.Store; 27 28/** 29 * general class for handling a pkcs7-signature message. 30 * 31 * A simple example of usage - note, in the example below the validity of 32 * the certificate isn't verified, just the fact that one of the certs 33 * matches the given signer... 34 * 35 * <pre> 36 * Store certStore = s.getCertificates(); 37 * SignerInformationStore signers = s.getSignerInfos(); 38 * Collection c = signers.getSigners(); 39 * Iterator it = c.iterator(); 40 * 41 * while (it.hasNext()) 42 * { 43 * SignerInformation signer = (SignerInformation)it.next(); 44 * Collection certCollection = certStore.getMatches(signer.getSID()); 45 * 46 * Iterator certIt = certCollection.iterator(); 47 * X509CertificateHolder cert = (X509CertificateHolder)certIt.next(); 48 * 49 * if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) 50 * { 51 * verified++; 52 * } 53 * } 54 * </pre> 55 */ 56public class CMSSignedData 57{ 58 private static final CMSSignedHelper HELPER = CMSSignedHelper.INSTANCE; 59 60 SignedData signedData; 61 ContentInfo contentInfo; 62 CMSTypedData signedContent; 63 SignerInformationStore signerInfoStore; 64 65 private Map hashes; 66 67 private CMSSignedData( 68 CMSSignedData c) 69 { 70 this.signedData = c.signedData; 71 this.contentInfo = c.contentInfo; 72 this.signedContent = c.signedContent; 73 this.signerInfoStore = c.signerInfoStore; 74 } 75 76 public CMSSignedData( 77 byte[] sigBlock) 78 throws CMSException 79 { 80 this(CMSUtils.readContentInfo(sigBlock)); 81 } 82 83 public CMSSignedData( 84 CMSProcessable signedContent, 85 byte[] sigBlock) 86 throws CMSException 87 { 88 this(signedContent, CMSUtils.readContentInfo(sigBlock)); 89 } 90 91 /** 92 * Content with detached signature, digests precomputed 93 * 94 * @param hashes a map of precomputed digests for content indexed by name of hash. 95 * @param sigBlock the signature object. 96 */ 97 public CMSSignedData( 98 Map hashes, 99 byte[] sigBlock) 100 throws CMSException 101 { 102 this(hashes, CMSUtils.readContentInfo(sigBlock)); 103 } 104 105 /** 106 * base constructor - content with detached signature. 107 * 108 * @param signedContent the content that was signed. 109 * @param sigData the signature object. 110 */ 111 public CMSSignedData( 112 CMSProcessable signedContent, 113 InputStream sigData) 114 throws CMSException 115 { 116 this(signedContent, CMSUtils.readContentInfo(new ASN1InputStream(sigData))); 117 } 118 119 /** 120 * base constructor - with encapsulated content 121 */ 122 public CMSSignedData( 123 InputStream sigData) 124 throws CMSException 125 { 126 this(CMSUtils.readContentInfo(sigData)); 127 } 128 129 public CMSSignedData( 130 final CMSProcessable signedContent, 131 ContentInfo sigData) 132 throws CMSException 133 { 134 if (signedContent instanceof CMSTypedData) 135 { 136 this.signedContent = (CMSTypedData)signedContent; 137 } 138 else 139 { 140 this.signedContent = new CMSTypedData() 141 { 142 public ASN1ObjectIdentifier getContentType() 143 { 144 return signedData.getEncapContentInfo().getContentType(); 145 } 146 147 public void write(OutputStream out) 148 throws IOException, CMSException 149 { 150 signedContent.write(out); 151 } 152 153 public Object getContent() 154 { 155 return signedContent.getContent(); 156 } 157 }; 158 } 159 160 this.contentInfo = sigData; 161 this.signedData = getSignedData(); 162 } 163 164 public CMSSignedData( 165 Map hashes, 166 ContentInfo sigData) 167 throws CMSException 168 { 169 this.hashes = hashes; 170 this.contentInfo = sigData; 171 this.signedData = getSignedData(); 172 } 173 174 public CMSSignedData( 175 ContentInfo sigData) 176 throws CMSException 177 { 178 this.contentInfo = sigData; 179 this.signedData = getSignedData(); 180 181 // 182 // this can happen if the signed message is sent simply to send a 183 // certificate chain. 184 // 185 if (signedData.getEncapContentInfo().getContent() != null) 186 { 187 this.signedContent = new CMSProcessableByteArray(signedData.getEncapContentInfo().getContentType(), 188 ((ASN1OctetString)(signedData.getEncapContentInfo() 189 .getContent())).getOctets()); 190 } 191 else 192 { 193 this.signedContent = null; 194 } 195 } 196 197 private SignedData getSignedData() 198 throws CMSException 199 { 200 try 201 { 202 return SignedData.getInstance(contentInfo.getContent()); 203 } 204 catch (ClassCastException e) 205 { 206 throw new CMSException("Malformed content.", e); 207 } 208 catch (IllegalArgumentException e) 209 { 210 throw new CMSException("Malformed content.", e); 211 } 212 } 213 214 /** 215 * Return the version number for this object 216 */ 217 public int getVersion() 218 { 219 return signedData.getVersion().getValue().intValue(); 220 } 221 222 /** 223 * return the collection of signers that are associated with the 224 * signatures for the message. 225 */ 226 public SignerInformationStore getSignerInfos() 227 { 228 if (signerInfoStore == null) 229 { 230 ASN1Set s = signedData.getSignerInfos(); 231 List signerInfos = new ArrayList(); 232 SignatureAlgorithmIdentifierFinder sigAlgFinder = new DefaultSignatureAlgorithmIdentifierFinder(); 233 234 for (int i = 0; i != s.size(); i++) 235 { 236 SignerInfo info = SignerInfo.getInstance(s.getObjectAt(i)); 237 ASN1ObjectIdentifier contentType = signedData.getEncapContentInfo().getContentType(); 238 239 if (hashes == null) 240 { 241 signerInfos.add(new SignerInformation(info, contentType, signedContent, null)); 242 } 243 else 244 { 245 Object obj = hashes.keySet().iterator().next(); 246 byte[] hash = (obj instanceof String) ? (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm().getId()) : (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm()); 247 248 signerInfos.add(new SignerInformation(info, contentType, null, hash)); 249 } 250 } 251 252 signerInfoStore = new SignerInformationStore(signerInfos); 253 } 254 255 return signerInfoStore; 256 } 257 258 /** 259 * Return any X.509 certificate objects in this SignedData structure as a Store of X509CertificateHolder objects. 260 * 261 * @return a Store of X509CertificateHolder objects. 262 */ 263 public Store getCertificates() 264 { 265 return HELPER.getCertificates(signedData.getCertificates()); 266 } 267 268 /** 269 * Return any X.509 CRL objects in this SignedData structure as a Store of X509CRLHolder objects. 270 * 271 * @return a Store of X509CRLHolder objects. 272 */ 273 public Store getCRLs() 274 { 275 return HELPER.getCRLs(signedData.getCRLs()); 276 } 277 278 /** 279 * Return any X.509 attribute certificate objects in this SignedData structure as a Store of X509AttributeCertificateHolder objects. 280 * 281 * @return a Store of X509AttributeCertificateHolder objects. 282 */ 283 public Store getAttributeCertificates() 284 { 285 return HELPER.getAttributeCertificates(signedData.getCertificates()); 286 } 287 288 // BEGIN android-removed 289 // /** 290 // * Return any OtherRevocationInfo OtherRevInfo objects of the type indicated by otherRevocationInfoFormat in 291 // * this SignedData structure. 292 // * 293 // * @param otherRevocationInfoFormat OID of the format type been looked for. 294 // * 295 // * @return a Store of ASN1Encodable objects representing any objects of otherRevocationInfoFormat found. 296 // */ 297 // public Store getOtherRevocationInfo(ASN1ObjectIdentifier otherRevocationInfoFormat) 298 // { 299 // return HELPER.getOtherRevocationInfo(otherRevocationInfoFormat, signedData.getCRLs()); 300 // } 301 // END android-removed 302 303 /** 304 * Return the a string representation of the OID associated with the 305 * encapsulated content info structure carried in the signed data. 306 * 307 * @return the OID for the content type. 308 */ 309 public String getSignedContentTypeOID() 310 { 311 return signedData.getEncapContentInfo().getContentType().getId(); 312 } 313 314 public CMSTypedData getSignedContent() 315 { 316 return signedContent; 317 } 318 319 /** 320 * return the ContentInfo 321 */ 322 public ContentInfo toASN1Structure() 323 { 324 return contentInfo; 325 } 326 327 /** 328 * return the ASN.1 encoded representation of this object. 329 */ 330 public byte[] getEncoded() 331 throws IOException 332 { 333 return contentInfo.getEncoded(); 334 } 335 336 // BEGIN android-removed 337 // /** 338 // * Verify all the SignerInformation objects and their associated counter signatures attached 339 // * to this CMS SignedData object. 340 // * 341 // * @param verifierProvider a provider of SignerInformationVerifier objects. 342 // * @return true if all verify, false otherwise. 343 // * @throws CMSException if an exception occurs during the verification process. 344 // */ 345 // public boolean verifySignatures(SignerInformationVerifierProvider verifierProvider) 346 // throws CMSException 347 // { 348 // return verifySignatures(verifierProvider, false); 349 // } 350 // 351 // /** 352 // * Verify all the SignerInformation objects and optionally their associated counter signatures attached 353 // * to this CMS SignedData object. 354 // * 355 // * @param verifierProvider a provider of SignerInformationVerifier objects. 356 // * @param ignoreCounterSignatures if true don't check counter signatures. If false check counter signatures as well. 357 // * @return true if all verify, false otherwise. 358 // * @throws CMSException if an exception occurs during the verification process. 359 // */ 360 // public boolean verifySignatures(SignerInformationVerifierProvider verifierProvider, boolean ignoreCounterSignatures) 361 // throws CMSException 362 // { 363 // Collection signers = this.getSignerInfos().getSigners(); 364 // 365 // for (Iterator it = signers.iterator(); it.hasNext();) 366 // { 367 // SignerInformation signer = (SignerInformation)it.next(); 368 // 369 // try 370 // { 371 // SignerInformationVerifier verifier = verifierProvider.get(signer.getSID()); 372 // 373 // if (!signer.verify(verifier)) 374 // { 375 // return false; 376 // } 377 // 378 // if (!ignoreCounterSignatures) 379 // { 380 // Collection counterSigners = signer.getCounterSignatures().getSigners(); 381 // 382 // for (Iterator cIt = counterSigners.iterator(); cIt.hasNext();) 383 // { 384 // SignerInformation counterSigner = (SignerInformation)cIt.next(); 385 // SignerInformationVerifier counterVerifier = verifierProvider.get(signer.getSID()); 386 // 387 // if (!counterSigner.verify(counterVerifier)) 388 // { 389 // return false; 390 // } 391 // } 392 // } 393 // } 394 // catch (OperatorCreationException e) 395 // { 396 // throw new CMSException("failure in verifier provider: " + e.getMessage(), e); 397 // } 398 // } 399 // 400 // return true; 401 // } 402 // END android-removed 403 404 /** 405 * Replace the SignerInformation store associated with this 406 * CMSSignedData object with the new one passed in. You would 407 * probably only want to do this if you wanted to change the unsigned 408 * attributes associated with a signer, or perhaps delete one. 409 * 410 * @param signedData the signed data object to be used as a base. 411 * @param signerInformationStore the new signer information store to use. 412 * @return a new signed data object. 413 */ 414 public static CMSSignedData replaceSigners( 415 CMSSignedData signedData, 416 SignerInformationStore signerInformationStore) 417 { 418 // 419 // copy 420 // 421 CMSSignedData cms = new CMSSignedData(signedData); 422 423 // 424 // replace the store 425 // 426 cms.signerInfoStore = signerInformationStore; 427 428 // 429 // replace the signers in the SignedData object 430 // 431 ASN1EncodableVector digestAlgs = new ASN1EncodableVector(); 432 ASN1EncodableVector vec = new ASN1EncodableVector(); 433 434 Iterator it = signerInformationStore.getSigners().iterator(); 435 while (it.hasNext()) 436 { 437 SignerInformation signer = (SignerInformation)it.next(); 438 digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID())); 439 vec.add(signer.toASN1Structure()); 440 } 441 442 ASN1Set digests = new DERSet(digestAlgs); 443 ASN1Set signers = new DERSet(vec); 444 ASN1Sequence sD = (ASN1Sequence)signedData.signedData.toASN1Primitive(); 445 446 vec = new ASN1EncodableVector(); 447 448 // 449 // signers are the last item in the sequence. 450 // 451 vec.add(sD.getObjectAt(0)); // version 452 vec.add(digests); 453 454 for (int i = 2; i != sD.size() - 1; i++) 455 { 456 vec.add(sD.getObjectAt(i)); 457 } 458 459 vec.add(signers); 460 461 cms.signedData = SignedData.getInstance(new BERSequence(vec)); 462 463 // 464 // replace the contentInfo with the new one 465 // 466 cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData); 467 468 return cms; 469 } 470 471 /** 472 * Replace the certificate and CRL information associated with this 473 * CMSSignedData object with the new one passed in. 474 * 475 * @param signedData the signed data object to be used as a base. 476 * @param certificates the new certificates to be used. 477 * @param attrCerts the new attribute certificates to be used. 478 * @param crls the new CRLs to be used. 479 * @return a new signed data object. 480 * @exception CMSException if there is an error processing the CertStore 481 */ 482 public static CMSSignedData replaceCertificatesAndCRLs( 483 CMSSignedData signedData, 484 Store certificates, 485 Store attrCerts, 486 Store crls) 487 throws CMSException 488 { 489 // 490 // copy 491 // 492 CMSSignedData cms = new CMSSignedData(signedData); 493 494 // 495 // replace the certs and crls in the SignedData object 496 // 497 ASN1Set certSet = null; 498 ASN1Set crlSet = null; 499 500 if (certificates != null || attrCerts != null) 501 { 502 List certs = new ArrayList(); 503 504 if (certificates != null) 505 { 506 certs.addAll(CMSUtils.getCertificatesFromStore(certificates)); 507 } 508 if (attrCerts != null) 509 { 510 certs.addAll(CMSUtils.getAttributeCertificatesFromStore(attrCerts)); 511 } 512 513 ASN1Set set = CMSUtils.createBerSetFromList(certs); 514 515 if (set.size() != 0) 516 { 517 certSet = set; 518 } 519 } 520 521 if (crls != null) 522 { 523 ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(crls)); 524 525 if (set.size() != 0) 526 { 527 crlSet = set; 528 } 529 } 530 531 // 532 // replace the CMS structure. 533 // 534 cms.signedData = new SignedData(signedData.signedData.getDigestAlgorithms(), 535 signedData.signedData.getEncapContentInfo(), 536 certSet, 537 crlSet, 538 signedData.signedData.getSignerInfos()); 539 540 // 541 // replace the contentInfo with the new one 542 // 543 cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData); 544 545 return cms; 546 } 547} 548