Java X509 certificate parsing and verification
I'm trying to handle X509 certificates in several steps and have run into two issues. I'm new to JCE so I'm not fully up to date yet.
We want to be able to parse several different X509 certificates based on different encodings (PEM, DER and PCKS7). I have exported the same certificate (certificate including chain) in PEM and PCKS7 format using FireFox from https://belgium.be . I put a few lines that the question doesn't need
public List<X509Certificate> parse(FileInputStream fis) {
/*
* Generate a X509 Certificate initialized with the data read from the inputstream.
* NOTE: Generation fails when using BufferedInputStream on PKCS7 certificates.
*/
List<X509Certificate> certificates = null;
log.debug("Parsing new certificate.");
certificates = (List<X509Certificate>) cf.generateCertificates(fis);
return certificates;
}
This code works fine as long as I use FileInputStream instead of PCKS7's BufferedInputStream, I think it's weird already? But I can live with it.
The next step is to verify these certificate chains. 1) Check if all certificates have an expiry date (easy) 2) Verify the certificate chain using OCSP (fallback to CRL if OCSP URL is not found in certificate). This is where I'm not sure how to go about it.
I'm using Sun JCE, but there doesn't seem to be much documentation available (in examples)?
I first made a simple implementation that just checks the chain without OCSP/CRL checking.
private Boolean validateChain(List<X509Certificate> certificates) {
PKIXParameters params;
CertPath certPath;
CertPathValidator certPathValidator;
Boolean valid = Boolean.FALSE;
params = new PKIXParameters(keyStore);
params.setRevocationEnabled(false);
certPath = cf.generateCertPath(certificates);
certPathValidator = CertPathValidator.getInstance("PKIX");
PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)
certPathValidator.validate(certPath, params);
if(null != result) {
valid = Boolean.TRUE;
}
return valid;
}
This works fine for my PEM certificate but not for PCKS7 certificate (same certificate, only exported in other format). java.security.cert.CertPathValidatorException: Path is not linked with any trust anchor.
The only difference I can see is that the CertPaths are not formed in the same order? I can't figure out what's wrong, so I'm leaving for now and moving on to the PEM certificate, but let's call this problem 1 ;)
After that what I want to achieve is OCSP check. Apparently if I enable OCSP with: Security.setProperty("ocsp.enable", "true"); and set params.setRevocationEnabled(true); it should be able to find the OCSP URL by itself, but it doesn't. What should the standard implementation do (question 2)? java.security.cert.CertPathValidatorException: The location of the OCSP responder must be specified
Going beyond this, I found a way to retrieve the OCSP URL from the certificate using AuthorityInfoAccessExtension etc.
But after manually setting the OCSP url in the ocsp.url property, I get java.security.cert.CertPathValidatorException: OCSP response error: Unauthorized
It seems like I'm missing many necessary steps, and many online references say that setting the ocsp.enable property should be all you need?
Maybe none of you guides can guide me through this process? tell me i'm totally wrong :)
The next step will be to perform a CRL check, if OCSP is not found, if anyone can point me to any examples or show me some documentation, that would also be greatly appreciated!
Thanks!
EDIT: Since it's not self-selecting properties by itself, I've been trying to set all properties myself using:
// Activate OCSP
Security.setProperty("ocsp.enable", "true");
// Activate CRLDP -- no idea what this is
Security.setProperty("com.sun.security.enableCRLDP", "true");
X509Certificate target = (X509Certificate) certPath.getCertificates().get(0);
Security.setProperty("ocsp.responderURL","http://ocsp.pki.belgium.be/");
Security.setProperty("ocsp.responderCertIssuerName", target.getIssuerX500Principal().getName());
Security.setProperty("ocsp.responderCertSubjectName", target.getSubjectX500Principal().getName());
Security.setProperty("ocsp.responderCertSerialNumber", target.getSerialNumber().toString(16));
An exception occurred: java.security.cert.CertPathValidatorException: The responder's certificate (set with the OCSP security attribute) could not be found.
For future reference, I'll post the answer to my own question (partially at least)
OCSP and CRL checks are already implemented in the standard Java implementation and do not require custom code or other providers (BC, ..). They are disabled by default.
To do this, you must set at least two parameters:
(PKIXParameters or PKIXParameterBuilder) params.setRevocationEnabled(true);
Security.setProperty("ocsp.enable", "true");
This activates the OCSP check when you try to validate the certificate path (PKIXCertPathValidatorResult.validate()).
If you want to add a fallback check for CRL when no OCSP is available, add additional properties:
System.setProperty("com.sun.security.enableCRLDP", "true");
A lot of problems happened since I had to support different certificate formats (PKCS7, PEM). My implementation works fine for PEM, but it's a little harder since PKCS7 doesn't preserve the order of certificates in the chain ( http://bugs.sun.com/view_bug.do?bug_id=6238093 )
X509CertSelector targetConstraints = new X509CertSelector();
targetConstraints.setCertificate(certificates.get(0));
// Here's the issue for PKCS7 certificates since they are not ordered,
// but I havent figured out how I can see what the target certificate
// (lowest level) is in the incoming certificates..
PKIXBuilderParameters params = new PKIXBuilderParameters(anchors, targetConstraints);
Hope this is useful to others too, maybe someone can shed some light on how to find the target certificate in the unordered PKCS7 list?