使用bouncycastle签署和validation签名的正确方法

我使用bcmail-jdk16-1.46.jarbcprov-jdk16-1.46.jar (Bouncycastle库)来签名string ,然后validationsignature

这是我签署string code

 package my.package; import java.io.FileInputStream; import java.security.Key; import java.security.KeyStore; import java.security.PrivateKey; import java.security.Security; import java.security.Signature; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import org.bouncycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSSignedDataGenerator; import org.bouncycastle.cms.CMSTypedData; import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.util.Store; import sun.misc.BASE64Encoder; public class SignMessage { static final String KEYSTORE_FILE = "keys/certificates.p12"; static final String KEYSTORE_INSTANCE = "PKCS12"; static final String KEYSTORE_PWD = "test"; static final String KEYSTORE_ALIAS = "Key1"; public static void main(String[] args) throws Exception { String text = "This is a message"; Security.addProvider(new BouncyCastleProvider()); KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE); ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD.toCharArray()); Key key = ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD.toCharArray()); //Sign PrivateKey privKey = (PrivateKey) key; Signature signature = Signature.getInstance("SHA1WithRSA", "BC"); signature.initSign(privKey); signature.update(text.getBytes()); //Build CMS X509Certificate cert = (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS); List certList = new ArrayList(); CMSTypedData msg = new CMSProcessableByteArray(signature.sign()); certList.add(cert); Store certs = new JcaCertStore(certList); CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privKey); gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer, cert)); gen.addCertificates(certs); CMSSignedData sigData = gen.generate(msg, false); BASE64Encoder encoder = new BASE64Encoder(); String signedContent = encoder.encode((byte[]) sigData.getSignedContent().getContent()); System.out.println("Signed content: " + signedContent + "\n"); String envelopedData = encoder.encode(sigData.getEncoded()); System.out.println("Enveloped data: " + envelopedData); } } 

现在, EnvelopedData输出将在此过程中用于通过以下方式verify signature

 package my.package; import java.security.Security; import java.security.cert.X509Certificate; import java.util.Collection; import java.util.Iterator; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.Store; import org.bouncycastle.util.encoders.Base64; public class VerifySignature { public static void main(String[] args) throws Exception { String envelopedData = "MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIAwggLQMIIC" + "OQIEQ479uzANBgkqhkiG9w0BAQUFADCBrjEmMCQGCSqGSIb3DQEJARYXcm9zZXR0YW5ldEBtZW5k" + "ZWxzb24uZGUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEi" + "MCAGA1UEChMZbWVuZGVsc29uLWUtY29tbWVyY2UgR21iSDEiMCAGA1UECxMZbWVuZGVsc29uLWUt" + "Y29tbWVyY2UgR21iSDENMAsGA1UEAxMEbWVuZDAeFw0wNTEyMDExMzQyMTlaFw0xOTA4MTAxMzQy" + "MTlaMIGuMSYwJAYJKoZIhvcNAQkBFhdyb3NldHRhbmV0QG1lbmRlbHNvbi5kZTELMAkGA1UEBhMC" + "REUxDzANBgNVBAgTBkJlcmxpbjEPMA0GA1UEBxMGQmVybGluMSIwIAYDVQQKExltZW5kZWxzb24t" + "ZS1jb21tZXJjZSBHbWJIMSIwIAYDVQQLExltZW5kZWxzb24tZS1jb21tZXJjZSBHbWJIMQ0wCwYD" + "VQQDEwRtZW5kMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+X1g6JvbdwJI6mQMNT41GcycH" + "UbwCFWKJ4qHDaHffz3n4h+uQJJoQvc8yLTCfnl109GB0yL2Y5YQtTohOS9IwyyMWBhh77WJtCN8r" + "dOfD2DW17877te+NlpugRvg6eOH6np9Vn3RZODVxxTyyJ8pI8VMnn13YeyMMw7VVaEO5hQIDAQAB" + "MA0GCSqGSIb3DQEBBQUAA4GBALwOIc/rWMAANdEh/GgO/DSkVMwxM5UBr3TkYbLU/5jg0Lwj3Y++" + "KhumYSrxnYewSLqK+JXA4Os9NJ+b3eZRZnnYQ9eKeUZgdE/QP9XE04y8WL6ZHLB4sDnmsgVaTU+p" + "0lFyH0Te9NyPBG0J88109CXKdXCTSN5gq0S1CfYn0staAAAxggG9MIIBuQIBATCBtzCBrjEmMCQG" + "CSqGSIb3DQEJARYXcm9zZXR0YW5ldEBtZW5kZWxzb24uZGUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQI" + "EwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEiMCAGA1UEChMZbWVuZGVsc29uLWUtY29tbWVyY2Ug" + "R21iSDEiMCAGA1UECxMZbWVuZGVsc29uLWUtY29tbWVyY2UgR21iSDENMAsGA1UEAxMEbWVuZAIE" + "Q479uzAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx" + "DxcNMTMwNTIxMDE1MDUzWjAjBgkqhkiG9w0BCQQxFgQU8mE6gw6iudxLUc9379lWK0lUSWcwDQYJ" + "KoZIhvcNAQEBBQAEgYB5mVhqJu1iX9nUqfqk7hTYJb1lR/hQiCaxruEuInkuVTglYuyzivZjAR54" + "zx7Cfm5lkcRyyxQ35ztqoq/V5JzBa+dYkisKcHGptJX3CbmmDIa1s65mEye4eLS4MTBvXCNCUTb9" + "STYSWvr4VPenN80mbpqSS6JpVxjM0gF3QTAhHwAAAAAAAA=="; Security.addProvider(new BouncyCastleProvider()); CMSSignedData cms = new CMSSignedData(Base64.decode(envelopedData.getBytes())); Store store = cms.getCertificates(); SignerInformationStore signers = cms.getSignerInfos(); Collection c = signers.getSigners(); Iterator it = c.iterator(); while (it.hasNext()) { SignerInformation signer = (SignerInformation) it.next(); Collection certCollection = store.getMatches(signer.getSID()); Iterator certIt = certCollection.iterator(); X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next(); X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder); if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) { System.out.println("verified"); } } } } 

由于以下Exception一切正常,直到signer.verify(..)

 Exception in thread "main" org.bouncycastle.cms.CMSSignerDigestMismatchException: message-digest attribute value does not match calculated value at org.bouncycastle.cms.SignerInformation.doVerify(Unknown Source) at org.bouncycastle.cms.SignerInformation.verify(Unknown Source) at my.package.VerifySignature.main(VerifySignature.java:64) 

我真的不知道我做错了什么。 有人可以给我一些关于发生了什么的提示吗?


PS 。 如果有人想测试上面的code ,你需要我用来复制所有这些的测试certificate文件,只需从这里download/save

https://dl.dropboxusercontent.com/u/15208254/keys/certificates.p12

 gen.generate(msg, false) 

表示签名数据未封装在签名中。 如果你想创建一个分离的签名,这很好,但它确实意味着当你去validationSignedData时,你必须使用CMSSignedData构造函数来获取数据的副本 – 在这种情况下代码使用单个必须假设已签名数据被封装的参数构造函数(因此对于这种情况将为空),结果是validation尝试失败。

使用CMSSignedDataGenerator生成两种CMSSignedData对象它们通过以下方式生成:

下面的一个生成一个携带分离的CMS签名的CMSSignedData对象

 gen.generate(cmsdata); 

下面的代码创建一个带有分离的CMS签名的CMSSignedData,其中包含数据

 gen.generate(cmsdata, true); 

因此validation它们需要两种方法

方法1用validation封装数据的分离签名

 //sig is the Signature object CMSSignedData signedData = new CMSSignedData(Base64.decode(sig.getBytes())); 

方法2validation没有封装数据的分离签名,只是分离签名

 //Create a CMSProcessable object, specify any encoding, I have used mine CMSProcessable signedContent = new CMSProcessableByteArray(content.getBytes("ISO-8859-1")); //Create a InputStream object InputStream is = new ByteArrayInputStream(Base64.decode(sig.getBytes())); //Pass them both to CMSSignedData constructor CMSSignedData signedData = new CMSSignedData(signedContent, is); 

其余的validation代码保持不变

 Store store = signedData.getCertificates(); SignerInformationStore signers = signedData.getSignerInfos(); Collection c = signers.getSigners(); Iterator it = c.iterator(); while (it.hasNext()) { SignerInformation signer = (SignerInformation)it.next(); Collection certCollection = store.getMatches(signer.getSID()); Iterator certIt = certCollection.iterator(); X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next(); X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder); if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) { ret = true; } } 

如果我们在OP的答案中使用signature.sign(),我们将无法检索原始消息,因为它只是签名。

您应该只输入原始文本字节而不是签名内容。 基本上,忽略这一部分:

 //Sign PrivateKey privKey = (PrivateKey) key; Signature signature = Signature.getInstance("SHA1WithRSA", "BC"); signature.initSign(privKey); signature.update(text.getBytes()); 

并输入为:

 CMSTypedData msg = new CMSProcessableByteArray(text.getBytes()); 

你可以在这里找到这个链接的答案你只需要添加一些标题到消息或只是在消息之前添加一个空行然后签署消息然后它将正常工作。

得到了分离签名:D

 package signature; import java.security.Provider; import java.security.Security; import java.security.cert.X509Certificate; import java.util.Collection; import java.util.Iterator; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cms.CMSProcessable; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.Store; import org.bouncycastle.util.encoders.Base64; public class VerifySignature { static final String DIGEST_SHA1 = "SHA1withRSA"; static final String BC_PROVIDER = "BC"; public static void main(String[] args) throws Exception { String envelopedData = "MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIAwggLQMIIC" + "OQIEQ479uzANBgkqhkiG9w0BAQUFADCBrjEmMCQGCSqGSIb3DQEJARYXcm9zZXR0YW5ldEBtZW5k" + "ZWxzb24uZGUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEi" + "MCAGA1UEChMZbWVuZGVsc29uLWUtY29tbWVyY2UgR21iSDEiMCAGA1UECxMZbWVuZGVsc29uLWUt" + "Y29tbWVyY2UgR21iSDENMAsGA1UEAxMEbWVuZDAeFw0wNTEyMDExMzQyMTlaFw0xOTA4MTAxMzQy" + "MTlaMIGuMSYwJAYJKoZIhvcNAQkBFhdyb3NldHRhbmV0QG1lbmRlbHNvbi5kZTELMAkGA1UEBhMC" + "REUxDzANBgNVBAgTBkJlcmxpbjEPMA0GA1UEBxMGQmVybGluMSIwIAYDVQQKExltZW5kZWxzb24t" + "ZS1jb21tZXJjZSBHbWJIMSIwIAYDVQQLExltZW5kZWxzb24tZS1jb21tZXJjZSBHbWJIMQ0wCwYD" + "VQQDEwRtZW5kMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+X1g6JvbdwJI6mQMNT41GcycH" + "UbwCFWKJ4qHDaHffz3n4h+uQJJoQvc8yLTCfnl109GB0yL2Y5YQtTohOS9IwyyMWBhh77WJtCN8r" + "dOfD2DW17877te+NlpugRvg6eOH6np9Vn3RZODVxxTyyJ8pI8VMnn13YeyMMw7VVaEO5hQIDAQAB" + "MA0GCSqGSIb3DQEBBQUAA4GBALwOIc/rWMAANdEh/GgO/DSkVMwxM5UBr3TkYbLU/5jg0Lwj3Y++" + "KhumYSrxnYewSLqK+JXA4Os9NJ+b3eZRZnnYQ9eKeUZgdE/QP9XE04y8WL6ZHLB4sDnmsgVaTU+p" + "0lFyH0Te9NyPBG0J88109CXKdXCTSN5gq0S1CfYn0staAAAxggG9MIIBuQIBATCBtzCBrjEmMCQG" + "CSqGSIb3DQEJARYXcm9zZXR0YW5ldEBtZW5kZWxzb24uZGUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQI" + "EwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEiMCAGA1UEChMZbWVuZGVsc29uLWUtY29tbWVyY2Ug" + "R21iSDEiMCAGA1UECxMZbWVuZGVsc29uLWUtY29tbWVyY2UgR21iSDENMAsGA1UEAxMEbWVuZAIE" + "Q479uzAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx" + "DxcNMTMwNTIxMDE1MDUzWjAjBgkqhkiG9w0BCQQxFgQU8mE6gw6iudxLUc9379lWK0lUSWcwDQYJ" + "KoZIhvcNAQEBBQAEgYB5mVhqJu1iX9nUqfqk7hTYJb1lR/hQiCaxruEuInkuVTglYuyzivZjAR54" + "zx7Cfm5lkcRyyxQ35ztqoq/V5JzBa+dYkisKcHGptJX3CbmmDIa1s65mEye4eLS4MTBvXCNCUTb9" + "STYSWvr4VPenN80mbpqSS6JpVxjM0gF3QTAhHwAAAAAAAA=="; String Sig_Bytes ="YduK22AlMLSXV3ajX5r/pX5OQ0xjj58uhGT9I9MvOrz912xNHo+9OiOKeMOD+Ys2/LUW3XaN6T+/"+ "tuRM5bi4RK7yjaqaJCZWtr/O4I968BQGgt0cyNvK8u0Jagbr9MYk6G7nnejbRXYHyAOaunqD05lW"+ "U/+g92i18dl0OMc50m4="; Provider provider = new BouncyCastleProvider(); Security.addProvider(provider); CMSSignedData signedData = new CMSSignedData(Base64.decode(envelopedData.getBytes())); CMSProcessable cmsProcesableContent = new CMSProcessableByteArray(Base64.decode(Sig_Bytes.getBytes())); signedData = new CMSSignedData(cmsProcesableContent, Base64.decode(envelopedData.getBytes())); // Verify signature Store store = signedData.getCertificates(); SignerInformationStore signers = signedData.getSignerInfos(); Collection c = signers.getSigners(); Iterator it = c.iterator(); while (it.hasNext()) { SignerInformation signer = (SignerInformation) it.next(); Collection certCollection = store.getMatches(signer.getSID()); Iterator certIt = certCollection.iterator(); X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next(); X509Certificate certFromSignedData = new JcaX509CertificateConverter().setProvider(BC_PROVIDER).getCertificate(certHolder); if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC_PROVIDER).build(certFromSignedData))) { System.out.println("Signature verified"); } else { System.out.println("Signature verification failed"); } } } }