5
votes

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.

2
Why don't you let the trust manager via the JSSE do all this for you?Bruno
Because my server is also accepting non cert-auth connection. I have to let it accept all connections first and then in some case validate the client's certificate if any.Ghetolay
No, you just need to make the client certificate optional using the wantClientAuth option (instead of need).Bruno
Actually it's how it work, but I thought it wasnt doing any validation on the client's certificate. I've done some test after reading your comment and this is how its seems to work: If a valid client certificate is sent by the client, the connection is accepted and the request attribute X509Certificate is set with the valid certificate. If a invalid or no certificate are sent then the connection is still accepted but the request attribute is not set. Is this is right it means I just need to test the request attribute in order to know if the certificate is valid. Can you confirm this behavior?Ghetolay
Yes, that's how it's meant to work when you configure the client-cert authentication to be optional. I think the connection would be rejected if the certificate isn't trusted (i.e. the client shouldn't send a cert at all if its cert isn't trusted).Bruno

2 Answers

0
votes

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 !

-1
votes

Please check your certificate CRL or OCSP is accessible, you can find such information in certificate, such as

[1]CRL Distribution Point
     Distribution Point Name:
          Full Name:
               URL=http://crl.verisign.com/pca2-g2.crl