Java/Keystore Verify Signed certificate

Ghetolay picture Ghetolay · Sep 17, 2012 · Viewed 8.8k times · Source

I'm working on a client-cert authentication between a embedded jetty server and a client. They both use keystore. The client certificate is signed by the server's certificate which is signed by a CA. Jetty use 2 method to authenticate a client certificate, javax.net.ssl.SSLEngine which seems to work and they also use the code above.

List<X509Certificate> certList = Certificate chain sent by the client
KeyStore truststore = server's truststore

//No use of CRL/OSCP/CRLDP
_crls = null;
_enableOCSP = false;
_enableCRLDP = false;

try{
 X509CertSelector certSelect = new X509CertSelector();
 certSelect.setCertificate((X509Certificate) certList.get(0));

 // Configure certification path builder parameters
 PKIXBuilderParameters pbParams = new PKIXBuilderParameters(truststore, certSelect);
 pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList)));

 // Set maximum certification path length
 pbParams.setMaxPathLength(-1);

 // Enable revocation checking
 pbParams.setRevocationEnabled(true);

 // Set static Certificate Revocation List
 if (_crls != null && !_crls.isEmpty())
     pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(_crls)));

  // Enable On-Line Certificate Status Protocol (OCSP) support
  if (_enableOCSP)
      Security.setProperty("ocsp.enable","true");

  // Enable Certificate Revocation List Distribution Points (CRLDP) support
  if (_enableCRLDP)
      System.setProperty("com.sun.security.enableCRLDP","true");

 // Build certification path
 CertPathBuilderResult buildResult = CertPathBuilder.getInstance("PKIX").build(pbParams);               

 // Validate certification path
 CertPathValidator.getInstance("PKIX").validate(buildResult.getCertPath(),pbParams);
}catch(GeneralSecurityException gse){
 ...
}

Of course I must use this second way... So let's concentrate on this code, is this a good way to verify a signed certificate ? Here is a dump of my keystores :

Client Keystore :

Entry type: PrivateKeyEntry 
Certificate chain length: 2
Certificate[1]: 
Owner: [email protected], CN=Servlet, OU=dev, O=Imbasoft, ST=Ile-de-France, C=FR 
Issuer: [email protected], CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR 
...

Certificate[2]: 
Owner: [email protected], CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR
Issuer: [email protected], CN=Greenpacs Certificate Authority, OU=dev, O=Imbasoft, ST=Ile-de-France, C=FR 
...

Server truststore :

Entry type: trustedCertEntry

Owner: [email protected], CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR
Issuer: [email protected], CN=Greenpacs Certificate Authority, OU=dev, O=Imbasoft, ST=Ile-de-France, C=FR

I'm not sure about these keystores but I tried with different one (adding the CA certificate to the client's certificate chain, adding certificate to the truststore) and the validation still fail. And with these keystores the first way of validation (SSLEngine) seems to work.

The debug output is too big to put it here but here is the stacktrace :

java.security.cert.CertPathValidatorException: Could not determine revocation status
    at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:153)
    at sun.security.provider.certpath.PKIXCertPathValidator.doValidate(PKIXCertPathValidator.java:325)
    at sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:187)
    at java.security.cert.CertPathValidator.validate(CertPathValidator.java:267)
    at MainClass.main(MainClass.java:75)
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:197)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
    at sun.security.provider.certpath.CrlRevocationChecker.buildToNewKey(CrlRevocationChecker.java:583)
    at sun.security.provider.certpath.CrlRevocationChecker.verifyWithSeparateSigningKey(CrlRevocationChecker.java:459)
    at sun.security.provider.certpath.CrlRevocationChecker.verifyRevocationStatus(CrlRevocationChecker.java:339)
    at sun.security.provider.certpath.CrlRevocationChecker.verifyRevocationStatus(CrlRevocationChecker.java:248)
    at sun.security.provider.certpath.CrlRevocationChecker.check(CrlRevocationChecker.java:189)
    at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:131)
    ... 4 more

If I disable the revocation or if I set the last certificate (instead of the first) as X509CertSelector the code work but I'm not sure of what I'm doing.

I'm starting to doubt about the jetty code but I'm not an expert in certificate and SSL handshaking so it could also come from bad keystore/truststore. That's why I did not create a issue on jetty's board and asked here before, to be sure the code needs to be changed.

Also It could be useful to know how to validate a signed certificate in Java.

Answer

Ghetolay picture Ghetolay · Sep 17, 2012

Actually I don't need to do the validation myself.

The SSLEngine is already doing it. If a valid certificate is sent by the client you can get it using getPeerCertificateChain(): if no certificate or an invalid certificate is sent by the client getPeerCertificateChain() throws a exception.

Using Jetty (or any Java ServletContainer I guess) you just need to check the HttpServletRequest's attribute["javax.servlet.request.X509Certificate"] to know if a valid certificate was sent by the client.

I still don't know how to validate a certificate in Java but this solution is enough for me :) I don't need to do it myself anymore. Thanks to Bruno !