Bro*_*oks 5 java cryptography bouncycastle digital-signature
我一直在用这个问题敲打墙头约20个小时,我可能会错过一些简单的事情.但是,我已经到了我认为需要帮助的地步.我已经阅读了几十个关于如何解决问题的不同部分的解释,但我无法弄清楚如何将它们整合在一起.
我有一个DER编码的分离式PKCS#7数字签名.签名符合RFC 3852(加密消息语法).对于我的项目,我需要逐步完成验证签名所需的每个步骤,并能够确定验证失败的步骤.我在Java中使用BouncyCastle.
据我了解,验证数字签名需要六个基本步骤
编辑:几条评论要求在列表中添加OSCP检查.
在BouncyCastle测试代码中,我能够找到以下示例.它似乎完成了2/6,然而,它并不清楚它是否完成了任何任务.如果有人能指出我正确的方向完成剩下的任务,我们将不胜感激.
CMSSignedData s = ...
byte[] contentDigest = ...
Store certStore = s.getCertificates();
Store crlStore = s.getCRLs();
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certStore.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC).build(cert)));
if (contentDigest != null)
{
assertTrue(MessageDigest.isEqual(contentDigest, signer.getContentDigest()));
}
}
Collection certColl = certStore.getMatches(null);
Collection crlColl = crlStore.getMatches(null);
assertEquals(certColl.size(), s.getCertificates().getMatches(null).size());
assertEquals(crlColl.size(), s.getCRLs().getMatches(null).size());
Run Code Online (Sandbox Code Playgroud)
CMS签名数据验证中最复杂的是X.509验证部分.对于签名数据中的每个签名者,步骤如下:
1.找到签名者证书
SignerInformation signerInfo = (SignerInformation)it.next();
Collection certCollection = certStore.getMatches(signerInfo.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder signerCertificateHolder = (X509CertificateHolder)certIt.next();
Run Code Online (Sandbox Code Playgroud)
我假设这里只有一个匹配的证书.
2.查找从签署者证书到受信任根证书的证书链
在这里,您要检查证书Store,以便找到证书链.使用主题DN /颁发者DN和主题密钥标识符/授权密钥标识符匹配来构建该链.
3.验证链
此步骤对链中的每个证书进行递归验证.对于每个证书,从直接在根证书下的证书开始,您需要检查:
希望对于步骤2和3,您可以使用内置的PKIX证书路径构建器和验证机制:
KeyStore trustAnchors = getTrustAnchors();
X509CertSelector target = new X509CertSelector();
target.setCertificate(signerCertificate);
PKIXBuilderParameters params = new PKIXBuilderParameters(anchors, target);
CertStoreParameters additionalCerts = new CollectionCertStoreParameters(allOtherCerts)
params.addCertStore(CertStore.getInstance("Collection", additionalCerts));
CertStoreParameters revocationObjects = new CollectionCertStoreParameters(allCRLs);
params.addCertStore(CertStore.getInstance("Collection", revocationObjects));
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
PKIXCertPathBuilderResult r = (PKIXCertPathBuilderResult) builder.build(params);
/* if the build method returns without exception, the certificate chain is valid */
Run Code Online (Sandbox Code Playgroud)
为了便于阅读,我省略java(x).crypto了Bouncycastle类型之间的对象转换.
4.使用公钥验证SignerInfo签名
JcaSimpleSignerInfoVerifierBuilder builder = new JcaSimpleSignerInfoVerifierBuilder();
SignerInformationVerifier verifier = builder.build(signerCertificateHolder);
assertTrue(signerInfo.verify(verifier));
Run Code Online (Sandbox Code Playgroud)
5.验证文档摘要是否与签名摘要匹配
byte[] contentDigest = computeDigest(originalDoc, signerInfo.getDigestAlgOID());
assertArrayEquals(contentDigest, signer.getContentDigest());
Run Code Online (Sandbox Code Playgroud)