java ssl keystore lets-encrypt ca

¿Java admite los certificados Let''s Encrypt?



ssl keystore (4)

Estoy desarrollando una aplicación Java que consulta una API REST en un servidor remoto a través de HTTP. Por razones de seguridad, esta comunicación debe cambiarse a HTTPS.

Ahora que Let''s Encrypt comenzó su versión beta pública, me gustaría saber si Java funciona actualmente (o se confirma que funcionará en el futuro) con sus certificados de forma predeterminada.

Let''s Encrypt consiguió su firma cruzada intermedia por IdenTrust , lo que debería ser una buena noticia. Sin embargo, no puedo encontrar ninguno de estos dos en la salida de este comando:

keytool -keystore "../lib/security/cacerts" -storepass changeit -list

Sé que las CA de confianza se pueden agregar manualmente en cada máquina, pero dado que mi aplicación debería poder descargarse y ejecutarse sin ninguna configuración adicional, estoy buscando soluciones que funcionen "fuera de la caja". ¿Tienes buenas noticias para mí?


Para los JDK que aún no admiten los certificados Let''s Encrypt, puede agregarlos a los cacerts JDK siguiendo este proceso (gracias a this ).

Descargue todos los certificados en https://letsencrypt.org/certificates/ (elija el formato der ) y agréguelos uno por uno con este tipo de comando (ejemplo para letsencryptauthorityx1.der ):

keytool -import -keystore PATH_TO_JDK/jre/lib/security/cacerts -storepass changeit -noprompt -trustcacerts -alias letsencryptauthorityx1 -file PATH_TO_DOWNLOADS/letsencryptauthorityx1.der


Respuesta detallada para aquellos de nosotros que estamos dispuestos a hacer cambios de configuración local que incluyen hacer una copia de seguridad del archivo de configuración:

1. Pruebe si está funcionando antes de los cambios

Si todavía no tiene un programa de prueba, puede usar mi programa de ping SSLPing de Java que prueba el protocolo de enlace TLS (funcionará con cualquier puerto SSL / TLS, no solo HTTPS). Usaré SSLPing.jar precompilado, pero leer el código y construirlo usted mismo es una tarea rápida y fácil:

$ git clone https://github.com/dimalinux/SSLPing.git Cloning into ''SSLPing''... [... output snipped ...]

Dado que mi versión de Java es anterior a 1.8.0_101 (no publicada en el momento de escribir este artículo), un certificado Let''s Encrypt no se verificará de manera predeterminada. Veamos cómo se ve la falla antes de aplicar la corrección:

$ java -jar SSLPing/dist/SSLPing.jar helloworld.letsencrypt.org 443 About to connect to ''helloworld.letsencrypt.org'' on port 443 javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target [... output snipped ...]

2. Importar el certificado

Estoy en Mac OS X con el conjunto de variables de entorno JAVA_HOME. Los comandos posteriores supondrán que esta variable está establecida para la instalación de Java que está modificando:

$ echo $JAVA_HOME /Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/

Haga una copia de seguridad del archivo cacerts que modificaremos para que pueda hacer una copia de seguridad de cualquier cambio sin reinstalar el JDK:

$ sudo cp -a $JAVA_HOME/jre/lib/security/cacerts $JAVA_HOME/jre/lib/security/cacerts.orig

Descargue el certificado de firma que necesitamos importar:

$ wget https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.der

Realizar la importación:

$ sudo keytool -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -noprompt -importcert -alias lets-encrypt-x3-cross-signed -file lets-encrypt-x3-cross-signed.der Certificate was added to keystore

3. Verifique que esté funcionando después de los cambios

Verifique que Java ahora esté contento de conectarse al puerto SSL:

$ java -jar SSLPing/dist/SSLPing.jar helloworld.letsencrypt.org 443 About to connect to ''helloworld.letsencrypt.org'' on port 443 Successfully connected



[ Actualización 2016-06-08 : De acuerdo con https://bugs.openjdk.java.net/browse/JDK-8154757 IdenTrust CA se incluirá en Oracle Java 8u101.]

[ Actualización 2016-08-05 : Java 8u101 ha sido lanzado y de hecho incluye IdenTrust CA: notas de la versión ]

¿Java admite los certificados Let''s Encrypt?

Si. El certificado Let''s Encrypt es solo un certificado de clave pública normal. Java lo admite (según letsencrypt.org/docs/certificate-compatibility , para Java 7> = 7u111 y Java 8> = 8u101).

¿Java confía en los certificados Let''s Encrypt listos para usar?

No / depende de la JVM. El almacén de confianza de Oracle JDK / JRE hasta 8u66 no contiene el Let''s Encrypt CA específicamente ni el IdenTrust CA que lo firmaron. new URL("https://letsencrypt.org/").openConnection().connect(); por ejemplo, da como resultado javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException .

Sin embargo, puede proporcionar su propio validador / definir un almacén de claves personalizado que contenga la CA raíz requerida o importar el certificado al almacén de confianza JVM.

https://community.letsencrypt.org/t/will-the-cross-root-cover-trust-by-the-default-list-in-the-jdk-jre/134/10 discute el tema.

Aquí hay un código de ejemplo que muestra cómo agregar un certificado al almacén de confianza predeterminado en tiempo de ejecución. Solo tendrá que agregar el certificado (exportado desde Firefox como .der y puesto en classpath)

Basado en ¿Cómo puedo obtener una lista de certificados raíz de confianza en Java? y http://developer.android.com/training/articles/security-ssl.html#UnknownCa

import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.PKIXParameters; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.TrustManagerFactory; public class SSLExample { // BEGIN ------- ADDME static { try { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); Path ksPath = Paths.get(System.getProperty("java.home"), "lib", "security", "cacerts"); keyStore.load(Files.newInputStream(ksPath), "changeit".toCharArray()); CertificateFactory cf = CertificateFactory.getInstance("X.509"); try (InputStream caInput = new BufferedInputStream( // this files is shipped with the application SSLExample.class.getResourceAsStream("DSTRootCAX3.der"))) { Certificate crt = cf.generateCertificate(caInput); System.out.println("Added Cert for " + ((X509Certificate) crt) .getSubjectDN()); keyStore.setCertificateEntry("DSTRootCAX3", crt); } if (false) { // enable to see System.out.println("Truststore now trusting: "); PKIXParameters params = new PKIXParameters(keyStore); params.getTrustAnchors().stream() .map(TrustAnchor::getTrustedCert) .map(X509Certificate::getSubjectDN) .forEach(System.out::println); System.out.println(); } TrustManagerFactory tmf = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keyStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); SSLContext.setDefault(sslContext); } catch (Exception e) { throw new RuntimeException(e); } } // END ---------- ADDME public static void main(String[] args) throws IOException { // signed by default trusted CAs. testUrl(new URL("https://google.com")); testUrl(new URL("https://www.thawte.com")); // signed by letsencrypt testUrl(new URL("https://helloworld.letsencrypt.org")); // signed by LE''s cross-sign CA testUrl(new URL("https://letsencrypt.org")); // expired testUrl(new URL("https://tv.eurosport.com/")); // self-signed testUrl(new URL("https://www.pcwebshop.co.uk/")); } static void testUrl(URL url) throws IOException { URLConnection connection = url.openConnection(); try { connection.connect(); System.out.println("Headers of " + url + " => " + connection.getHeaderFields()); } catch (SSLHandshakeException e) { System.out.println("Untrusted: " + url); } } }