DoNuT DoNuT - 4 months ago 33
Java Question

Java SSL Certificate Revocation Checking

I'm currently writing a network TCP server using SSL. In production, we'll finally require clients to authenticate with a certificate.

In order to revoke certificates in case of a emergency, we would also like to establish a CRL.

My question is: Does Java check CRLs (if provided with the certificate) out of the box or do I need to manually implement such checks?

For testing, I prepared a certificate with a CRL set but Java does not seem to try to validate it (I dropped it into a local web server and there's no access).

I only found the com.sun.net.ssl.checkRevocation=true VM option, but apparently it doesn't query the CRL. VM debugging set to java.security.debug=certpath does not generate any output, either...

Java seems to have related classes in its subsystems (e.g. java.security.cert.X509CRLSelector), but it does not come into play, obviously.

I wrote a small maven-style project using Apache Mina as client server that initializes a SSLContext based on key/truststores and self-signed certificates for client/server which can be downloaded here as ZIP archive: https://www.dropbox.com/s/3fqmd1v9mn2a5ve/ssltest.zip?dl=0

Answer

I figured how to enable CRL checking within a SSLContext without implementing a custom validator, as suggested in the comments.

It is mainly about properly initializing the SSLContext's TrustManagers with a revocation checker, only a few lines, no custom check logic and the CRL is now checked automatically as well as the verification path.

Here's a snippet...

KeyStore ts = KeyStore.getInstance("JKS");
FileInputStream tfis = new FileInputStream(trustStorePath);
ts.load(tfis, trustStorePass.toCharArray());

KeyManagerFactory kmf =  KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

// initialize certification path checking for the offered certificates and revocation checks against CLRs
CertPathBuilder cpb = CertPathBuilder.getInstance("PKIX");
PKIXRevocationChecker rc = (PKIXRevocationChecker)cpb.getRevocationChecker();
rc.setOptions(EnumSet.of(
    PKIXRevocationChecker.Option.PREFER_CRLS, // prefer CLR over OCSP
    PKIXRevocationChecker.Option.ONLY_END_ENTITY, 
    PKIXRevocationChecker.Option.SOFT_FAIL, // handshake should not fail when CRL is not available
PKIXRevocationChecker.Option.NO_FALLBACK)); // don't fall back to OCSP checking

PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(ts, new X509CertSelector());
pkixParams.addCertPathChecker(rc);

tmf.init( new CertPathTrustManagerParameters(pkixParams) );
// init KeyManagerFactory
kmf.init(...)

SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers), tmf.getTrustManagers(), null);

That essentially did what I needed in my application, checking whether a certificate issued to a client is revoked in our CRL. Only checking the end entity and allowing the CRL check to fail is accepted because its all our infrastructure.