使用PKCS#7加密

我正在使用Bouncy Castle提供的库来加密,解密,签名和validation签名。 我这样做
1.加密数据
2.签署数据
3.将带符号的字节写入文件
4.从文件中读取带符号的字节
5.validation签名
6.解密数据

我参考了Beginning Cryptography with Java

当我validation数据时,我的问题出在第5步

org.bouncycastle.cms.CMSException:message-digest属性值与计算值不匹配

我的代码如下

import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.cert.CertPathBuilder; import java.security.cert.CertStore; import java.security.cert.Certificate; import java.security.cert.CollectionCertStoreParameters; import java.security.cert.PKIXBuilderParameters; import java.security.cert.PKIXCertPathBuilderResult; import java.security.cert.TrustAnchor; import java.security.cert.X509CertSelector; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.Iterator; import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500PrivateCredential; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.asn1.x509.X509Extensions; import org.bouncycastle.cms.CMSEnvelopedData; import org.bouncycastle.cms.CMSEnvelopedDataGenerator; import org.bouncycastle.cms.CMSEnvelopedDataParser; import org.bouncycastle.cms.CMSProcessable; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSSignedDataGenerator; import org.bouncycastle.cms.RecipientId; import org.bouncycastle.cms.RecipientInformation; import org.bouncycastle.cms.RecipientInformationStore; import org.bouncycastle.cms.SignerId; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.x509.X509V1CertificateGenerator; import org.bouncycastle.x509.X509V3CertificateGenerator; import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure; import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure; public class Test { private static final char[] KEY_STORE_PASSWORD = "123456".toCharArray(); private static final long VALIDITY_PERIOD = 365 * 24 * 60 * 60 * 1000; private static final char[] KEY_PASSWORD = "keyPassword".toCharArray(); public static String ROOT_ALIAS = "root"; public static String INTERMEDIATE_ALIAS = "intermediate"; public static String END_ENTITY_ALIAS = "end"; public static String PLAIN_TEXT = "Hello World!123"; public static void main(String[] args) { try{ // CREATE KEY STORE KeyStore keyStore = createKeyStore(); // STEP 1. ENCRYPT AND SIGN byte[] step1Data = encryptData(keyStore, PLAIN_TEXT.getBytes()); CMSSignedData cmsSignedData = signData(keyStore,step1Data); new File("D:\\pkcs7\\encrypted-file.p7b"); FileOutputStream fileOuputStream = new FileOutputStream("D:\\pkcs7\\encrypted-file.p7b"); fileOuputStream.write(cmsSignedData.getEncoded()); fileOuputStream.flush(); fileOuputStream.close(); // STEP 2. READ ENCRYPTED DATA AND VERIFY SIGN AND DECRYPT IT File file =new File("D:\\pkcs7\\encrypted-file.p7b"); FileInputStream fileInputStream = new FileInputStream(file); byte[] encryptedAndSignedByte = new byte[(int)file.length()]; fileInputStream.read(encryptedAndSignedByte ); fileInputStream.close(); cmsSignedData = new CMSSignedData(encryptedAndSignedByte); if( verifyData(keyStore, cmsSignedData) == true ){ decryptData(keyStore,encryptedAndSignedByte); } }catch (Exception e) { e.printStackTrace(); } } /** * * This method will encrypt data */ private static byte[] encryptData(KeyStore keyStore, byte[] plainData) throws Exception { PrivateKey key = (PrivateKey) keyStore.getKey(END_ENTITY_ALIAS, KEY_PASSWORD); Certificate[] chain = keyStore.getCertificateChain(END_ENTITY_ALIAS); X509Certificate cert = (X509Certificate) chain[0]; // set up the generator CMSEnvelopedDataGenerator gen = new CMSEnvelopedDataGenerator(); gen.addKeyTransRecipient(cert); // create the enveloped-data object CMSProcessable data = new CMSProcessableByteArray(plainData); CMSEnvelopedData enveloped = gen.generate(data, CMSEnvelopedDataGenerator.AES128_CBC, "BC"); return enveloped.getEncoded(); // recreate } private static byte[] decryptData(KeyStore keyStore,byte[] encryptedData) throws Exception{ CMSEnvelopedDataParser envelopedDataParser = new CMSEnvelopedDataParser(new ByteArrayInputStream(encryptedData)); PrivateKey key = (PrivateKey) keyStore.getKey(END_ENTITY_ALIAS,KEY_PASSWORD); Certificate[] chain = keyStore.getCertificateChain(END_ENTITY_ALIAS); X509Certificate cert = (X509Certificate) chain[0]; CMSEnvelopedData enveloped = new CMSEnvelopedData(encryptedData); // look for our recipient identifier RecipientId recId = new RecipientId(); recId.setSerialNumber(cert.getSerialNumber()); recId.setIssuer(cert.getIssuerX500Principal().getEncoded()); RecipientInformationStore recipients = enveloped.getRecipientInfos(); RecipientInformation recipient = recipients.get(recId); if (recipient != null) { // decrypt the data byte[] recData = recipient.getContent(key, "BC"); System.out.println("----------------------- RECOVERED DATA -----------------------"); System.out.println(new String(recData)); System.out.println("--------------------------------------------------------------"); return recData; } else { System.out.println("could not find a matching recipient"); } return null; } private static CMSSignedData signData(KeyStore keyStore,byte[] encryptedData ) throws Exception { // GET THE PRIVATE KEY PrivateKey key = (PrivateKey) keyStore.getKey(END_ENTITY_ALIAS, KEY_PASSWORD); Certificate[] chain = keyStore.getCertificateChain(END_ENTITY_ALIAS); CertStore certsAndCRLs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(Arrays.asList(chain)), "BC"); X509Certificate cert = (X509Certificate) chain[0]; // set up the generator CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); gen.addSigner(key, cert, CMSSignedDataGenerator.DIGEST_SHA224); gen.addCertificatesAndCRLs(certsAndCRLs); // create the signed-data object CMSProcessable data = new CMSProcessableByteArray(encryptedData); CMSSignedData signed = gen.generate(data, "BC"); // recreate signed = new CMSSignedData(data, signed.getEncoded()); // ContentInfo conInf = signed.getContentInfo(); // CMSProcessable sigContent = signed.getSignedContent(); return signed; } private static boolean verifyData(KeyStore keyStore, CMSSignedData signed) throws Exception { // verification step X509Certificate rootCert = (X509Certificate) keyStore.getCertificate(ROOT_ALIAS); if (isValidSignature(signed, rootCert)) { System.out.println("verification succeeded"); return true; } else { System.out.println("verification failed"); } return false; } /** * Take a CMS SignedData message and a trust anchor and determine if the * message is signed with a valid signature from a end entity entity * certificate recognized by the trust anchor rootCert. */ private static boolean isValidSignature(CMSSignedData signedData, X509Certificate rootCert) throws Exception { boolean[] bArr = new boolean[2]; bArr[0] = true; CertStore certsAndCRLs = signedData.getCertificatesAndCRLs( "Collection", "BC"); SignerInformationStore signers = signedData.getSignerInfos(); Iterator it = signers.getSigners().iterator(); if (it.hasNext()) { SignerInformation signer = (SignerInformation) it.next(); SignerId signerConstraints = signer.getSID(); signerConstraints.setKeyUsage(bArr); PKIXCertPathBuilderResult result = buildPath(rootCert, signer.getSID(), certsAndCRLs); return signer.verify(result.getPublicKey(), "BC"); } return false; } /** * Build a path using the given root as the trust anchor, and the passed in * end constraints and certificate store. * 

* Note: the path is built with revocation checking turned off. */ public static PKIXCertPathBuilderResult buildPath(X509Certificate rootCert, X509CertSelector endConstraints, CertStore certsAndCRLs) throws Exception { CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC"); PKIXBuilderParameters buildParams = new PKIXBuilderParameters( Collections.singleton(new TrustAnchor(rootCert, null)), endConstraints); buildParams.addCertStore(certsAndCRLs); buildParams.setRevocationEnabled(false); return (PKIXCertPathBuilderResult) builder.build(buildParams); } /** * Create a KeyStore containing the a private credential with certificate * chain and a trust anchor. */ public static KeyStore createKeyStore() throws Exception { KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(null, null); keyStore.load(null, null); X500PrivateCredential rootCredential = createRootCredential(); X500PrivateCredential interCredential = createIntermediateCredential( rootCredential.getPrivateKey(), rootCredential.getCertificate()); X500PrivateCredential endCredential = createEndEntityCredential( interCredential.getPrivateKey(), interCredential.getCertificate()); keyStore.setCertificateEntry(rootCredential.getAlias(), rootCredential.getCertificate()); keyStore.setKeyEntry( endCredential.getAlias(), endCredential.getPrivateKey(), KEY_PASSWORD, new Certificate[] { endCredential.getCertificate(), interCredential.getCertificate(), rootCredential.getCertificate() }); keyStore.store(new FileOutputStream("d:\\pkcs7\\KeyStore.jks"), KEY_STORE_PASSWORD); return keyStore; } /** * Create a random 1024 bit RSA key pair */ public static KeyPair generateRSAKeyPair() throws Exception { KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC"); kpGen.initialize(1024, new SecureRandom()); return kpGen.generateKeyPair(); } /** * Generate a sample V1 certificate to use as a CA root certificate */ public static X509Certificate generateCertificate(KeyPair pair) throws Exception { X509V1CertificateGenerator certGen = new X509V1CertificateGenerator(); certGen.setSerialNumber(BigInteger.valueOf(1)); certGen.setIssuerDN(new X500Principal("CN=Test CA Certificate")); certGen.setNotBefore(new Date(System.currentTimeMillis() - VALIDITY_PERIOD)); certGen.setNotAfter(new Date(System.currentTimeMillis() + VALIDITY_PERIOD)); certGen.setSubjectDN(new X500Principal("CN=Test CA Certificate")); certGen.setPublicKey(pair.getPublic()); certGen.setSignatureAlgorithm("SHA1WithRSAEncryption"); return certGen.generateX509Certificate(pair.getPrivate(), "BC"); } /** * Generate a sample V1 certificate to use as a CA root certificate */ public static X509Certificate generateRootCert(KeyPair pair) throws Exception { X509V1CertificateGenerator certGen = new X509V1CertificateGenerator(); certGen.setSerialNumber(BigInteger.valueOf(1)); certGen.setIssuerDN(new X500Principal("CN=Test CA Certificate")); certGen.setNotBefore(new Date(System.currentTimeMillis() - VALIDITY_PERIOD)); certGen.setNotAfter(new Date(System.currentTimeMillis() + VALIDITY_PERIOD)); certGen.setSubjectDN(new X500Principal("CN=Test CA Certificate")); certGen.setPublicKey(pair.getPublic()); certGen.setSignatureAlgorithm("SHA1WithRSAEncryption"); return certGen.generateX509Certificate(pair.getPrivate(), "BC"); } /** * Generate a sample V3 certificate to use as an end entity certificate */ public static X509Certificate generateEndEntityCert(PublicKey entityKey, PrivateKey caKey, X509Certificate caCert) throws Exception { X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); certGen.setSerialNumber(BigInteger.valueOf(1)); certGen.setIssuerDN(caCert.getSubjectX500Principal()); certGen.setNotBefore(new Date(System.currentTimeMillis() - VALIDITY_PERIOD)); certGen.setNotAfter(new Date(System.currentTimeMillis() + VALIDITY_PERIOD)); certGen.setSubjectDN(new X500Principal("CN=Test End Certificate")); certGen.setPublicKey(entityKey); certGen.setSignatureAlgorithm("SHA1WithRSAEncryption"); certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert)); certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(entityKey)); certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false)); certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage( KeyUsage.digitalSignature | KeyUsage.keyEncipherment)); return certGen.generateX509Certificate(caKey, "BC"); } /** * Generate a X500PrivateCredential for the root entity. */ public static X500PrivateCredential createRootCredential() throws Exception { KeyPair rootPair = generateRSAKeyPair(); X509Certificate rootCert = generateRootCert(rootPair); return new X500PrivateCredential(rootCert, rootPair.getPrivate(), ROOT_ALIAS); } /** * Generate a X500PrivateCredential for the intermediate entity. */ public static X500PrivateCredential createIntermediateCredential( PrivateKey caKey, X509Certificate caCert) throws Exception { KeyPair interPair = generateRSAKeyPair(); X509Certificate interCert = generateIntermediateCert( interPair.getPublic(), caKey, caCert); return new X500PrivateCredential(interCert, interPair.getPrivate(), INTERMEDIATE_ALIAS); } /** * Generate a X500PrivateCredential for the end entity. */ public static X500PrivateCredential createEndEntityCredential( PrivateKey caKey, X509Certificate caCert) throws Exception { KeyPair endPair = generateRSAKeyPair(); X509Certificate endCert = generateEndEntityCert(endPair.getPublic(), caKey, caCert); return new X500PrivateCredential(endCert, endPair.getPrivate(), END_ENTITY_ALIAS); } /** * Generate a sample V3 certificate to use as an intermediate CA certificate */ public static X509Certificate generateIntermediateCert(PublicKey intKey, PrivateKey caKey, X509Certificate caCert) throws Exception { X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); certGen.setSerialNumber(BigInteger.valueOf(1)); certGen.setIssuerDN(caCert.getSubjectX500Principal()); certGen.setNotBefore(new Date(System.currentTimeMillis())); certGen.setNotAfter(new Date(System.currentTimeMillis() + VALIDITY_PERIOD)); certGen.setSubjectDN(new X500Principal( "CN=Test Intermediate Certificate")); certGen.setPublicKey(intKey); certGen.setSignatureAlgorithm("SHA1WithRSAEncryption"); certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert)); certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(intKey)); certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(0)); certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage( KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); return certGen.generateX509Certificate(caKey, "BC"); } }

在典型用法中, .p7b文件仅包含公钥证书, .p7b包含私钥。 它通常用于存储整个证书链而不是单个证书。 ‘p7b’名称来自PKCS#7 SignedData结构的退化forms。 通常,私钥存储在PKCS#12(通常是具有.p12.pfx扩展名的文件)文件中,但其他格式也很常见。

要从p7b文件中读取证书,可以使用CertificateFactory类。 PKCS#12文件可直接用作密钥库。

你经常提到PKCS#7。 PKCS#7是一个非常大且开放的旧标准。 目前,更常用的标准是PKCS#7的扩展子集,称为CMS。 它是RFC 5652中记录的IETF标准。 Bouncycastle PKIX / CMS库对CMS规范提供了广泛的支持。

  try { File file = new File("d:\\TESTS\\VG.p7b"); FileInputStream fis = new FileInputStream(file); CertificateFactory cf = CertificateFactory.getInstance("X.509"); Collection c = cf.generateCertificates(fis); Iterator i = c.iterator(); while (i.hasNext()) { X509Certificate cert509 = (X509Certificate) i.next(); System.out.println(cert509); } } catch (Throwable th) { th.printStackTrace(); }