问题
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: EMAILADDRESS=truc@ok.com, CN=Servlet, OU=dev, O=Imbasoft, ST=Ile-de-France, C=FR
Issuer: EMAILADDRESS=contact@greenpacs.com, CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR
...
Certificate[2]:
Owner: EMAILADDRESS=contact@greenpacs.com, CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR
Issuer: EMAILADDRESS=ghetolay@imbasoft.com, CN=Greenpacs Certificate Authority, OU=dev, O=Imbasoft, ST=Ile-de-France, C=FR
...
Server truststore :
Entry type: trustedCertEntry
Owner: EMAILADDRESS=contact@greenpacs.com, CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR
Issuer: EMAILADDRESS=ghetolay@imbasoft.com, 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.
回答1:
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
回答2:
Actually I don't need to do the validation myself.
The SSLEngine is already doing it and 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() return null 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 !
来源:https://stackoverflow.com/questions/12456079/java-keystore-verify-signed-certificate