Need some help with crypto routines in Java.
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
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());
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()
will throw an exception if validation fails.
Docs: ASN1Set
, ContentInfo
, SignedData
. All other exotic names and related docs can be found in java.security.cert
.
No SUN-dependencies here, only BouncyCastle provider library is needed.
This question (and especially an answer) may help too.
You want CertificateFactory. The last example in the javadocs do exactly what you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With