Estoy trabajando en una autenticación de cliente-cert entre un servidor de embarcadero incrustado y un cliente. Ambos usan keystore. El certificado del cliente está firmado por el certificado del servidor que está firmado por una CA. Jetty usa el método 2 para autenticar un certificado de cliente, javax.net.ssl.SSLEngine que parece funcionar y también usan el código anterior.Java/Keystore Verificar Certificado firmado
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){
...
}
Por supuesto debo utilizar este segundo camino ... Así que vamos a concentrarnos en este código, se trata de una buena manera de verificar un certificado firmado? Aquí es un vertedero de mis almacenes de claves:
almacén de claves de cliente:
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
...
almacén de confianza del servidor:
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
No estoy seguro acerca de estos almacenes de claves, pero he intentado con uno diferente (añadiendo el Certificado de CA a la cadena de certificados del cliente, agregando certificado al almacén de confianza) y la validación sigue fallando. Y con estos almacenes de claves, la primera forma de validación (SSLEngine) parece funcionar.
El resultado de la depuración es demasiado grande como para poner aquí, pero aquí es la 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
Si desactivo la revocación o si fijo el último certificado (en lugar de la primera) como X509CertSelector que el código funcione pero no estoy seguro de lo que estoy haciendo.
Estoy empezando a dudar del código del muelle, pero no soy un experto en el protocolo y el protocolo de enlace SSL, por lo que también podría provenir del almacén de claves incorrecto/almacén de confianza. Es por eso que no creé un problema en el tablero de embarcadero y pregunté aquí antes, para asegurarme de que el código necesita ser cambiado.
También podría ser útil saber cómo validar un certificado firmado en Java.
¿Por qué no dejas que el administrador de confianza a través de JSSE haga todo esto por ti? – Bruno
Porque mi servidor también acepta conexiones no cert-auth. Debo dejar que acepte todas las conexiones primero y luego, en algún caso, validar el certificado del cliente, si corresponde. – Ghetolay
No, solo necesita hacer que el certificado del cliente sea opcional utilizando la opción wantClientAuth (en lugar de necesitarla). – Bruno