Java支持让我们encryption证书吗?

我正在开发一个通过HTTP与远程服务器进行通信的Java应用程序。

几个月前,我试图通过在服务器上安装一个StartSSL证书来切换到HTTPS。 像Chrome或Firefox这样的常见浏览器接受了证书,但是由于StartSSL不在Java的默认可信CA列表中,所以我的应用程序拒绝了连接。 最后,我回到使用HTTP。

现在让我们encryption开始他们的公开testing,我想知道是否Java当前工作(或被确认在未来工作)与他们的证书默认情况下。

让我们encryption得到他们的IdenTrust交叉签名 ,这应该是个好消息。 但是,在这个命令的输出中我找不到这两个中的任何一个:

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

我知道可信任的CA可以在每台机器上手动添加,但由于我的应用程序应该可以自由下载和执行,无需进一步的configuration,我正在寻找可以“开箱即用”的解决scheme。 你对我有好消息吗?

[ 更新2016-06-08 :根据https://bugs.openjdk.java.net/browse/JDK-8154757,IdenTrust CA将包含在Oracle Java 8u101中。]

[ 更新2016-08-05 :Java 8u101已经发布,确实包含了IdenTrust CA: 发行说明 ]


Java支持让我们encryption证书吗?

是。 让我们encryption证书只是一个普通的公钥证书。 Java支持它(根据Let's Encrypt Certificate Compatibility ,对于Java 7> = 7u111和Java 8> = 8u101)。

Java信任让我们encryption证书吗?

否/取决于JVM。 Oracle JDK / JRE的信任库高达8u66不包含Let's Encrypt CA,也不包含交叉签名的IdenTrust CA。 new URL("https://letsencrypt.org/").openConnection().connect(); 例如javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException结果javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException

但是,您可以提供自己的validation器/定义包含所需根CA的自定义密钥库,或者将证书导入JVM信任库。

https://community.letsencrypt.org/t/will-the-cross-root-cover-trust-by-the-default-list-in-the-jdk-jre/134/10也讨论了这个话题。;


以下是一些示例代码,演示了如何在运行时将证书添加到默认信任库。 你只需要添加证书(从Firefox导出为.der并放入类path)

基于如何获得Java中受信任的根证书列表? 和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); } } } 

我知道OP要求解决scheme没有本地configuration更改,但如果您想要永久地将信任链添加到密钥库:

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

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

对于我们这些愿意进行本地configuration更改(包括备份configuration文件)的详细答案:

1.在变化之前testing它是否正在工作

如果您还没有testing程序,则可以使用我的java SSLPing ping程序来testingTLS握手(可以使用任何SSL / TLS端口,而不仅仅是HTTPS)。 我将使用预构build的SSLPing.jar,但是阅读代码并自己构build它是一项快速而简单的任务:

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

由于我的Java版本早于1.8.0_101(在撰写本文时尚未发布),因此Let's Encrypt证书在默认情况下不会被validation。 让我们来看看在应用修复之前失败的样子:

 $ 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.导入证书

我在使用JAVA_HOME环境variables集的Mac OS X上。 稍后的命令会假定这个variables是为你正在修改的java安装设置的:

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

备份我们将要修改的cacerts文件,以便在不重新安装JDK的情况下退出任何更改:

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

下载我们需要导入的签名证书:

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

执行导入:

 $ 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.确认更改后它正在工作

validationJava现在很高兴连接到SSL端口:

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

对于不支持让我们对证书进行encryption的JDK,可以在这个过程之后将它们添加到JDK cacerts (感谢这个 )。

下载https://letsencrypt.org/certificates/上的所有证书(selectder格式),并使用这种命令( letsencryptauthorityx1.der示例) letsencryptauthorityx1.der

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