1 package org.bouncycastle.jce; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.ByteArrayOutputStream; 5 import java.io.IOException; 6 import java.math.BigInteger; 7 import java.security.InvalidKeyException; 8 import java.security.NoSuchAlgorithmException; 9 import java.security.NoSuchProviderException; 10 import java.security.PrivateKey; 11 import java.security.Signature; 12 import java.security.SignatureException; 13 import java.security.cert.CRL; 14 import java.security.cert.CRLException; 15 import java.security.cert.Certificate; 16 import java.security.cert.X509CRL; 17 import java.security.cert.X509Certificate; 18 import java.util.ArrayList; 19 import java.util.Collection; 20 import java.util.Enumeration; 21 import java.util.HashSet; 22 import java.util.Iterator; 23 import java.util.Set; 24 25 import org.bouncycastle.asn1.*; 26 import org.bouncycastle.asn1.pkcs.ContentInfo; 27 import org.bouncycastle.asn1.pkcs.IssuerAndSerialNumber; 28 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 29 import org.bouncycastle.asn1.pkcs.SignedData; 30 import org.bouncycastle.asn1.pkcs.SignerInfo; 31 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 32 import org.bouncycastle.asn1.x509.CertificateList; 33 import org.bouncycastle.asn1.x509.X509CertificateStructure; 34 import org.bouncycastle.asn1.x509.X509Name; 35 import org.bouncycastle.jce.provider.X509CRLObject; 36 import org.bouncycastle.jce.provider.X509CertificateObject; 37 38 /** 39 * Represents a PKCS#7 object - specifically the "Signed Data" 40 * type. 41 * <p> 42 * How to use it? To verify a signature, do: 43 * <pre> 44 * PKCS7SignedData pkcs7 = new PKCS7SignedData(der_bytes); // Create it 45 * pkcs7.update(bytes, 0, bytes.length); // Update checksum 46 * boolean verified = pkcs7.verify(); // Does it add up? 47 * 48 * To sign, do this: 49 * PKCS7SignedData pkcs7 = new PKCS7SignedData(privKey, certChain, "MD5"); 50 * pkcs7.update(bytes, 0, bytes.length); // Update checksum 51 * pkcs7.sign(); // Create digest 52 * 53 * bytes = pkcs7.getEncoded(); // Write it somewhere 54 * </pre> 55 * <p> 56 * This class is pretty close to obsolete, for a much better (and more complete) 57 * implementation of PKCS7 have a look at the org.bouncycastle.cms package. 58 * @deprecated this class really is obsolete - use the CMS package. 59 */ 60 public class PKCS7SignedData 61 implements PKCSObjectIdentifiers 62 { 63 private int version, signerversion; 64 private Set digestalgos; 65 private Collection certs, crls; 66 private X509Certificate signCert; 67 private byte[] digest; 68 private String digestAlgorithm, digestEncryptionAlgorithm; 69 private Signature sig; 70 private transient PrivateKey privKey; 71 72 private final String ID_PKCS7_DATA = "1.2.840.113549.1.7.1"; 73 private final String ID_PKCS7_SIGNED_DATA = "1.2.840.113549.1.7.2"; 74 private final String ID_MD5 = "1.2.840.113549.2.5"; 75 private final String ID_MD2 = "1.2.840.113549.2.2"; 76 private final String ID_SHA1 = "1.3.14.3.2.26"; 77 private final String ID_RSA = "1.2.840.113549.1.1.1"; 78 private final String ID_DSA = "1.2.840.10040.4.1"; 79 80 /** 81 * Read an existing PKCS#7 object from a DER encoded byte array using 82 * the BC provider. 83 */ 84 public PKCS7SignedData( 85 byte[] in) 86 throws SecurityException, CRLException, InvalidKeyException, 87 NoSuchProviderException, NoSuchAlgorithmException 88 { 89 this(in, "BC"); 90 } 91 92 /** 93 * Read an existing PKCS#7 object from a DER encoded byte array 94 */ 95 public PKCS7SignedData( 96 byte[] in, 97 String provider) 98 throws SecurityException, CRLException, InvalidKeyException, 99 NoSuchProviderException, NoSuchAlgorithmException 100 { 101 ASN1InputStream din = new ASN1InputStream(new ByteArrayInputStream(in)); 102 103 // 104 // Basic checks to make sure it's a PKCS#7 SignedData Object 105 // 106 DERObject pkcs; 107 108 try 109 { 110 pkcs = din.readObject(); 111 } 112 catch (IOException e) 113 { 114 throw new SecurityException("can't decode PKCS7SignedData object"); 115 } 116 117 if (!(pkcs instanceof ASN1Sequence)) 118 { 119 throw new SecurityException("Not a valid PKCS#7 object - not a sequence"); 120 } 121 122 ContentInfo content = ContentInfo.getInstance(pkcs); 123 124 if (!content.getContentType().equals(signedData)) 125 { 126 throw new SecurityException("Not a valid PKCS#7 signed-data object - wrong header " + content.getContentType().getId()); 127 } 128 129 130 SignedData data = SignedData.getInstance(content.getContent()); 131 132 certs = new ArrayList(); 133 134 if (data.getCertificates() != null) 135 { 136 Enumeration ec = ASN1Set.getInstance(data.getCertificates()).getObjects(); 137 138 while (ec.hasMoreElements()) 139 { 140 certs.add(new X509CertificateObject(X509CertificateStructure.getInstance(ec.nextElement()))); 141 } 142 } 143 144 crls = new ArrayList(); 145 146 if (data.getCRLs() != null) 147 { 148 Enumeration ec = ASN1Set.getInstance(data.getCRLs()).getObjects(); 149 while (ec.hasMoreElements()) 150 { 151 crls.add(new X509CRLObject(CertificateList.getInstance(ec.nextElement()))); 152 } 153 } 154 155 version = data.getVersion().getValue().intValue(); 156 157 // 158 // Get the digest algorithm 159 // 160 digestalgos = new HashSet(); 161 Enumeration e = data.getDigestAlgorithms().getObjects(); 162 163 while (e.hasMoreElements()) 164 { 165 ASN1Sequence s = (ASN1Sequence)e.nextElement(); 166 DERObjectIdentifier o = (DERObjectIdentifier)s.getObjectAt(0); 167 digestalgos.add(o.getId()); 168 } 169 170 // 171 // Get the SignerInfo 172 // 173 ASN1Set signerinfos = data.getSignerInfos(); 174 if (signerinfos.size() != 1) 175 { 176 throw new SecurityException("This PKCS#7 object has multiple SignerInfos - only one is supported at this time"); 177 } 178 179 SignerInfo signerInfo = SignerInfo.getInstance(signerinfos.getObjectAt(0)); 180 181 signerversion = signerInfo.getVersion().getValue().intValue(); 182 183 IssuerAndSerialNumber isAnds = signerInfo.getIssuerAndSerialNumber(); 184 185 // 186 // Get the signing certificate 187 // 188 BigInteger serialNumber = isAnds.getCertificateSerialNumber().getValue(); 189 X509Principal issuer = new X509Principal(isAnds.getName()); 190 191 for (Iterator i = certs.iterator();i.hasNext();) 192 { 193 X509Certificate cert = (X509Certificate)i.next(); 194 if (serialNumber.equals(cert.getSerialNumber()) 195 && issuer.equals(cert.getIssuerDN())) 196 { 197 signCert = cert; 198 break; 199 } 200 } 201 202 if (signCert == null) 203 { 204 throw new SecurityException("Can't find signing certificate with serial "+serialNumber.toString(16)); 205 } 206 207 digestAlgorithm = signerInfo.getDigestAlgorithm().getObjectId().getId(); 208 209 digest = signerInfo.getEncryptedDigest().getOctets(); 210 digestEncryptionAlgorithm = signerInfo.getDigestEncryptionAlgorithm().getObjectId().getId(); 211 212 sig = Signature.getInstance(getDigestAlgorithm(), provider); 213 214 sig.initVerify(signCert.getPublicKey()); 215 } 216 217 /** 218 * Create a new PKCS#7 object from the specified key using the BC provider. 219 * 220 * @param privKey the private key to be used for signing. 221 * @param certChain the certificate chain associated with the private key. 222 * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA" 223 */ 224 public PKCS7SignedData( 225 PrivateKey privKey, 226 Certificate[] certChain, 227 String hashAlgorithm) 228 throws SecurityException, InvalidKeyException, 229 NoSuchProviderException, NoSuchAlgorithmException 230 { 231 this(privKey, certChain, hashAlgorithm, "BC"); 232 } 233 234 /** 235 * Create a new PKCS#7 object from the specified key. 236 * 237 * @param privKey the private key to be used for signing. 238 * @param certChain the certificate chain associated with the private key. 239 * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA" 240 * @param provider the provider to use. 241 */ 242 public PKCS7SignedData( 243 PrivateKey privKey, 244 Certificate[] certChain, 245 String hashAlgorithm, 246 String provider) 247 throws SecurityException, InvalidKeyException, 248 NoSuchProviderException, NoSuchAlgorithmException 249 { 250 this(privKey, certChain, null, hashAlgorithm, provider); 251 } 252 253 /** 254 * Create a new PKCS#7 object from the specified key. 255 * 256 * @param privKey the private key to be used for signing. 257 * @param certChain the certificate chain associated with the private key. 258 * @param crlList the crl list associated with the private key. 259 * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA" 260 * @param provider the provider to use. 261 */ 262 public PKCS7SignedData( 263 PrivateKey privKey, 264 Certificate[] certChain, 265 CRL[] crlList, 266 String hashAlgorithm, 267 String provider) 268 throws SecurityException, InvalidKeyException, 269 NoSuchProviderException, NoSuchAlgorithmException 270 { 271 this.privKey = privKey; 272 273 if (hashAlgorithm.equals("MD5")) 274 { 275 digestAlgorithm = ID_MD5; 276 } 277 else if (hashAlgorithm.equals("MD2")) 278 { 279 digestAlgorithm = ID_MD2; 280 } 281 else if (hashAlgorithm.equals("SHA")) 282 { 283 digestAlgorithm = ID_SHA1; 284 } 285 else if (hashAlgorithm.equals("SHA1")) 286 { 287 digestAlgorithm = ID_SHA1; 288 } 289 else 290 { 291 throw new NoSuchAlgorithmException("Unknown Hash Algorithm "+hashAlgorithm); 292 } 293 294 version = signerversion = 1; 295 certs = new ArrayList(); 296 crls = new ArrayList(); 297 digestalgos = new HashSet(); 298 digestalgos.add(digestAlgorithm); 299 300 // 301 // Copy in the certificates and crls used to sign the private key. 302 // 303 signCert = (X509Certificate)certChain[0]; 304 for (int i = 0;i < certChain.length;i++) 305 { 306 certs.add(certChain[i]); 307 } 308 309 if (crlList != null) 310 { 311 for (int i = 0;i < crlList.length;i++) 312 { 313 crls.add(crlList[i]); 314 } 315 } 316 317 // 318 // Now we have private key, find out what the digestEncryptionAlgorithm is. 319 // 320 digestEncryptionAlgorithm = privKey.getAlgorithm(); 321 if (digestEncryptionAlgorithm.equals("RSA")) 322 { 323 digestEncryptionAlgorithm = ID_RSA; 324 } 325 else if (digestEncryptionAlgorithm.equals("DSA")) 326 { 327 digestEncryptionAlgorithm = ID_DSA; 328 } 329 else 330 { 331 throw new NoSuchAlgorithmException("Unknown Key Algorithm "+digestEncryptionAlgorithm); 332 } 333 334 sig = Signature.getInstance(getDigestAlgorithm(), provider); 335 336 sig.initSign(privKey); 337 } 338 339 /** 340 * Get the algorithm used to calculate the message digest 341 */ 342 public String getDigestAlgorithm() 343 { 344 String da = digestAlgorithm; 345 String dea = digestEncryptionAlgorithm; 346 347 if (digestAlgorithm.equals(ID_MD5)) 348 { 349 da = "MD5"; 350 } 351 else if (digestAlgorithm.equals(ID_MD2)) 352 { 353 da = "MD2"; 354 } 355 else if (digestAlgorithm.equals(ID_SHA1)) 356 { 357 da = "SHA1"; 358 } 359 360 if (digestEncryptionAlgorithm.equals(ID_RSA)) 361 { 362 dea = "RSA"; 363 } 364 else if (digestEncryptionAlgorithm.equals(ID_DSA)) 365 { 366 dea = "DSA"; 367 } 368 369 return da + "with" + dea; 370 } 371 372 /** 373 * Resets the PKCS7SignedData object to it's initial state, ready 374 * to sign or verify a new buffer. 375 */ 376 public void reset() 377 { 378 try 379 { 380 if (privKey==null) 381 { 382 sig.initVerify(signCert.getPublicKey()); 383 } 384 else 385 { 386 sig.initSign(privKey); 387 } 388 } 389 catch (Exception e) 390 { 391 throw new RuntimeException(e.toString()); 392 } 393 } 394 395 /** 396 * Get the X.509 certificates associated with this PKCS#7 object 397 */ 398 public Certificate[] getCertificates() 399 { 400 return (X509Certificate[])certs.toArray(new X509Certificate[certs.size()]); 401 } 402 403 /** 404 * Get the X.509 certificate revocation lists associated with this PKCS#7 object 405 */ 406 public Collection getCRLs() 407 { 408 return crls; 409 } 410 411 /** 412 * Get the X.509 certificate actually used to sign the digest. 413 */ 414 public X509Certificate getSigningCertificate() 415 { 416 return signCert; 417 } 418 419 /** 420 * Get the version of the PKCS#7 object. Always 1 421 */ 422 public int getVersion() 423 { 424 return version; 425 } 426 427 /** 428 * Get the version of the PKCS#7 "SignerInfo" object. Always 1 429 */ 430 public int getSigningInfoVersion() 431 { 432 return signerversion; 433 } 434 435 /** 436 * Update the digest with the specified byte. This method is used both for signing and verifying 437 */ 438 public void update(byte buf) 439 throws SignatureException 440 { 441 sig.update(buf); 442 } 443 444 /** 445 * Update the digest with the specified bytes. This method is used both for signing and verifying 446 */ 447 public void update(byte[] buf, int off, int len) 448 throws SignatureException 449 { 450 sig.update(buf, off, len); 451 } 452 453 /** 454 * Verify the digest 455 */ 456 public boolean verify() 457 throws SignatureException 458 { 459 return sig.verify(digest); 460 } 461 462 /** 463 * Get the "issuer" from the TBSCertificate bytes that are passed in 464 */ 465 private DERObject getIssuer(byte[] enc) 466 { 467 try 468 { 469 ASN1InputStream in = new ASN1InputStream(new ByteArrayInputStream(enc)); 470 ASN1Sequence seq = (ASN1Sequence)in.readObject(); 471 return (DERObject)seq.getObjectAt(seq.getObjectAt(0) instanceof DERTaggedObject ? 3 : 2); 472 } 473 catch (IOException e) 474 { 475 throw new Error("IOException reading from ByteArray: "+e); 476 } 477 } 478 479 /** 480 * return the bytes for the PKCS7SignedData object. 481 */ 482 public byte[] getEncoded() 483 { 484 try 485 { 486 487 digest = sig.sign(); 488 489 // Create the set of Hash algorithms. I've assumed this is the 490 // set of all hash agorithms used to created the digest in the 491 // "signerInfo" structure. I may be wrong. 492 // 493 ASN1EncodableVector v = new ASN1EncodableVector(); 494 for (Iterator i = digestalgos.iterator(); i.hasNext();) 495 { 496 AlgorithmIdentifier a = new AlgorithmIdentifier( 497 new DERObjectIdentifier((String)i.next()), 498 null); 499 500 v.add(a); 501 } 502 503 DERSet algos = new DERSet(v); 504 505 // Create the contentInfo. Empty, I didn't implement this bit 506 // 507 DERSequence contentinfo = new DERSequence( 508 new DERObjectIdentifier(ID_PKCS7_DATA)); 509 510 // Get all the certificates 511 // 512 v = new ASN1EncodableVector(); 513 for (Iterator i = certs.iterator();i.hasNext();) 514 { 515 ASN1InputStream tempstream = new ASN1InputStream(new ByteArrayInputStream(((X509Certificate)i.next()).getEncoded())); 516 v.add(tempstream.readObject()); 517 } 518 519 DERSet dercertificates = new DERSet(v); 520 521 // Create signerinfo structure. 522 // 523 ASN1EncodableVector signerinfo = new ASN1EncodableVector(); 524 525 // Add the signerInfo version 526 // 527 signerinfo.add(new DERInteger(signerversion)); 528 529 IssuerAndSerialNumber isAnds = new IssuerAndSerialNumber( 530 new X509Name((ASN1Sequence)getIssuer(signCert.getTBSCertificate())), 531 new DERInteger(signCert.getSerialNumber())); 532 signerinfo.add(isAnds); 533 534 // Add the digestAlgorithm 535 // 536 // BEGIN android-changed 537 signerinfo.add(new AlgorithmIdentifier( 538 new DERObjectIdentifier(digestAlgorithm), 539 DERNull.THE_ONE)); 540 541 // 542 // Add the digestEncryptionAlgorithm 543 // 544 signerinfo.add(new AlgorithmIdentifier( 545 new DERObjectIdentifier(digestEncryptionAlgorithm), 546 DERNull.THE_ONE)); 547 // END android-changed 548 549 // 550 // Add the digest 551 // 552 signerinfo.add(new DEROctetString(digest)); 553 554 555 // 556 // Finally build the body out of all the components above 557 // 558 ASN1EncodableVector body = new ASN1EncodableVector(); 559 body.add(new DERInteger(version)); 560 body.add(algos); 561 body.add(contentinfo); 562 body.add(new DERTaggedObject(false, 0, dercertificates)); 563 564 if (crls.size()>0) 565 { 566 v = new ASN1EncodableVector(); 567 for (Iterator i = crls.iterator();i.hasNext();) 568 { 569 ASN1InputStream t = new ASN1InputStream(new ByteArrayInputStream(((X509CRL)i.next()).getEncoded())); 570 v.add(t.readObject()); 571 } 572 DERSet dercrls = new DERSet(v); 573 body.add(new DERTaggedObject(false, 1, dercrls)); 574 } 575 576 // Only allow one signerInfo 577 // 578 body.add(new DERSet(new DERSequence(signerinfo))); 579 580 // Now we have the body, wrap it in it's PKCS7Signed shell 581 // and return it 582 // 583 ASN1EncodableVector whole = new ASN1EncodableVector(); 584 whole.add(new DERObjectIdentifier(ID_PKCS7_SIGNED_DATA)); 585 whole.add(new DERTaggedObject(0, new DERSequence(body))); 586 587 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 588 589 DEROutputStream dout = new DEROutputStream(bOut); 590 dout.writeObject(new DERSequence(whole)); 591 dout.close(); 592 593 return bOut.toByteArray(); 594 } 595 catch (Exception e) 596 { 597 throw new RuntimeException(e.toString()); 598 } 599 } 600 } 601