在 Java 中验证 PKCS#7 证书
在 Java 中的加密例程方面需要一些帮助.
Need some help with crypto routines in Java.
给定一个 PKCS#7 签名,我想根据受信任的存储验证它包含的所有证书.我假设签名中包含的所有证书都以正确的顺序形成有效的证书路径(或链,等等),所以
Given a PKCS#7 signature, I want to verify all certificates it contains against a trusted store. I assume that all certificates contained in signature are in the correct order to form a valid certificate path (or chain, whatever), so that
- 最上面 (#0) 是签名证书;
- 下一个 (#1) 是中间证书,用于签署 #0;
- 下一个 (#2) 是另一个中间证书,用于签署 #1;
- 等等.
最后一个证书 (#N) 由 CA 签名.
The last certificate (#N) is signed by CA.
到目前为止,这就是我设法破解的:
That's what I've managed to hack so far:
// Exception handling skipped for readability
//byte[] signature = ...
pkcs7 = new PKCS7(signature); // `sun.security.pkcs.PKCS7;`
// *** Checking some PKCS#7 parameters here
X509Certificate prevCert = null; // Previous certificate we've found
X509Certificate[] certs = pkcs7.getCertificates(); // `java.security.cert.X509Certificate`
for (int i = 0; i < certs.length; i++) {
// *** Checking certificate validity period here
if (cert != null) {
// Verify previous certificate in chain against this one
prevCert.verify(certs[i].getPublicKey());
}
prevCert = certs[i];
}
//String keyStorePath = ...
KeyStore keyStore = KeyStore.getInstance("JKS"); // `java.security.KeyStore`
keyStore.load(new FileInputStream(keyStorePath), null);
// Get trusted VeriSign class 1 certificate
Certificate caCert = keyStore.getCertificate("verisignclass1ca"); // `java.security.cert.Certificate`
// Verify last certificate against trusted certificate
cert.verify(caCert.getPublicKey());
所以问题是——如何使用像 CertPath
和朋友这样的标准 Java 类来做到这一点?我有一种强烈的感觉,我正在重新发明一辆自行车.或者,如果有人有 BouncyCastle 库的示例,那也可以.
So the question is -- how can this be done using standard Java classes like CertPath
and friends? I have a strong feeling I'm re-inventing a bicycle. Or, if someone has an example with BouncyCastle library, that would also be fine.
额外问题:如何根据受信任的存储验证证书以便自动选择根证书?
Bonus question: how to verify a certificate against a trusted store so that root certificate is selected automatically?
推荐答案
自己找到了解决方案.因此,以下是针对可信存储提取和验证证书链的方法(为便于阅读,跳过了异常处理):
Found the solution myself. So, here's how one can extract and validate a certificate chain against the trusted store (exception handling skipped for readability):
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// Get ContentInfo
//byte[] signature = ... // PKCS#7 signature bytes
InputStream signatureIn = new ByteArrayInputStream(signature);
DERObject obj = new ASN1InputStream(signatureIn).readObject();
ContentInfo contentInfo = ContentInfo.getInstance(obj);
// Extract certificates
SignedData signedData = SignedData.getInstance(contentInfo.getContent());
Enumeration certificates = signedData.getCertificates().getObjects();
// Build certificate path
List certList = new ArrayList();
while (certificates.hasMoreElements()) {
DERObject certObj = (DERObject) certificates.nextElement();
InputStream in = new ByteArrayInputStream(certObj.getDEREncoded());
certList.add(cf.generateCertificate(in));
}
CertPath certPath = cf.generateCertPath(certList);
// Load key store
//String keyStorePath = ...
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(keyStorePath), null);
// Set validation parameters
PKIXParameters params = new PKIXParameters(keyStore);
params.setRevocationEnabled(false); // to avoid exception on empty CRL
// Validate certificate path
CertPathValidator validator = CertPathValidator.getInstance("PKIX");
CertPathValidatorResult result = validator.validate(certPath, params);
validate()
如果验证失败会抛出异常.
validate()
will throw an exception if validation fails.
文档:<代码>ASN1Set, ContentInfo
, SignedData
.所有其他奇异名称和相关文档都可以在 java.security.cert
中找到.
这里没有 SUN 依赖项,只需要 BouncyCastle 提供程序库.
No SUN-dependencies here, only BouncyCastle provider library is needed.
这个问题(尤其是答案) 也可能有帮助.
This question (and especially an answer) may help too.
相关文章