O Java suporta Vamos Criptografar Certificados?

Estou desenvolvendo um aplicativo Java que consulta uma API REST em um servidor remoto por HTTP. Por motivos de segurança, esta comunicação deve ser comutada para HTTPS.

Agora que o Let’s Encrypt iniciou o beta público, gostaria de saber se o Java atualmente funciona (ou se está confirmado para funcionar no futuro) com seus certificados por padrão.

Vamos Criptografar obteve o seu intermediário cruzado pelo IdenTrust , o que deve ser uma boa notícia. No entanto, não consigo encontrar nenhum desses dois na saída deste comando:

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

Eu sei que as CAs confiáveis ​​podem ser adicionadas manualmente em cada máquina, mas como meu aplicativo deve ser gratuito para download e executável sem nenhuma configuração adicional, estou procurando soluções que funcionem “prontas para uso”. Você tem boas notícias para mim?

[ Atualização 2016-06-08 : De acordo com https://bugs.openjdk.java.net/browse/JDK-8154757, a CA IdenTrust será incluída no Oracle Java 8u101.]

[ Atualização 2016-08-05 : Java 8u101 foi lançado e inclui de fato o IDenTrust CA: notas de lançamento ]


O Java suporta Vamos Criptografar Certificados?

Sim. O certificado Let’s Encrypt é apenas um certificado de chave pública regular. O Java suporta-o (de acordo com Let’s Encrypt Certificate Compatibility , para Java 7> = 7u111 e Java 8> = 8u101).

O Java confia em Let’s Encrypt certificates out of the box?

Não / isso depende da JVM. O armazenamento confiável do Oracle JDK / JRE até 8u66 não contém a CA Vamos criptografar especificamente nem a CA IdenTrust que o assinou cruzadamente. new URL("https://letsencrypt.org/").openConnection().connect(); por exemplo, resulta em javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException .

No entanto, você pode fornecer seu próprio validador / definir um keystore customizado que contenha a CA raiz necessária ou importar o certificado no armazenamento confiável da JVM.

https://community.letsencrypt.org/t/will-the-cross-root-cover-trust-by-the-default-list-in-the-jdk-jre/134/10 discute o tópico também.


Aqui está um código de exemplo que mostra como include um certificado no armazenamento confiável padrão no tempo de execução. Você só precisa adicionar o certificado (exportado do firefox como .der e colocar no classpath)

Com base em Como posso obter uma lista de certificados raiz confiáveis ​​em Java? e 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); } } } 

Eu sei que o OP pediu uma solução sem alterações de configuração local, mas caso você queira adicionar a cadeia de confiança ao keystore permanentemente:

 $ keytool -trustcacerts \ -keystore $JAVA_HOME/jre/lib/security/cacerts \ -storepass changeit \ -noprompt \ -importcert \ -file /etc/letsencrypt/live/hostname.com/chain.pem 

fonte: https://community.letsencrypt.org/t/will-the-cross-root-cover-trust-by-the-default-list-in-the-jdk-jre/134/13

Resposta detalhada para aqueles que estão dispostos a fazer alterações nas configurações locais que incluem o backup do arquivo de configuração:

1. Teste se está funcionando antes das mudanças

Se você ainda não tem um programa de teste, pode usar meu programa ping JavaPING de java que testa o handshake TLS (funciona com qualquer porta SSL / TLS, não apenas com HTTPS). Usarei o SSLPing.jar pré-construído, mas ler o código e criá-lo você mesmo é uma tarefa rápida e fácil:

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

Como minha versão do Java é anterior à 1.8.0_101 (não liberada no momento da redação deste texto), um certificado Let’s Encrypt não será verificado por padrão. Vamos ver como é a falha antes de aplicar a correção:

 $ 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. Importe o certificado

Estou no Mac OS X com o conjunto de variables ​​de ambiente JAVA_HOME. Comandos posteriores irão assumir que esta variável está configurada para a instalação do java que você está modificando:

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

Faça um backup do arquivo cacerts que iremos modificar para que você possa fazer qualquer alteração sem reinstalar o JDK:

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

Faça o download do certificado de assinatura que precisamos importar:

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

Execute a importação:

 $ 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 se está funcionando após as alterações

Verifique se o Java está feliz em se conectar à porta SSL:

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

Para o JDK que não suporta os certificados Vamos Criptografar ainda, você pode adicioná-los aos cacerts do JDK após este processo (graças a isso ).

Faça o download de todos os certificados em https://letsencrypt.org/certificates/ (escolha o formato der ) e adicione-os um por um com este tipo de comando (exemplo 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