Java 是否支持 Let's Encrypt 证书?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34110426/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Does Java support Let's Encrypt certificates?
提问by Hexaholic
I am developing a Java application that queries a REST API on a remote server over HTTP. For security reasons this communication should be switched to HTTPS.
我正在开发一个 Java 应用程序,它通过 HTTP 查询远程服务器上的 REST API。出于安全原因,此通信应切换到 HTTPS。
Now that Let's Encryptstarted their public beta, I'd like to know if Java currently works (or is confirmed to be working in the future) with their certificates by default.
现在Let's Encrypt开始了他们的公开测试版,我想知道默认情况下 Java 当前是否可以(或确认将来可以)使用他们的证书。
Let's Encrypt got their intermediate cross-signed by IdenTrust, which should be good news. However, I cannot find any of these two in the output of this command:
Let's Encrypt 得到了IdenTrust的中间交叉签名,这应该是个好消息。但是,我在此命令的输出中找不到这两个中的任何一个:
keytool -keystore "..\lib\security\cacerts" -storepass changeit -list
I know that trusted CAs can be added manually on each machine, but since my application should be free to download and executable without any further configuration, I am looking for solutions that work "out of the box". Do you have good news for me?
我知道可以在每台机器上手动添加受信任的 CA,但是由于我的应用程序应该可以免费下载和执行而无需任何进一步配置,我正在寻找“开箱即用”的解决方案。你有什么好消息要告诉我吗?
采纳答案by zapl
[Update 2016-06-08: According to https://bugs.openjdk.java.net/browse/JDK-8154757the IdenTrust CA will be included in Oracle Java 8u101.]
[更新 2016-06-08:根据https://bugs.openjdk.java.net/browse/JDK-8154757,IdenTrustCA 将包含在 Oracle Java 8u101 中。]
[Update 2016-08-05: Java 8u101 has been released and does indeed include the IdenTrust CA: release notes]
[更新 2016-08-05:Java 8u101 已经发布并且确实包含 IdenTrust CA:发行说明]
Does Java support Let's Encrypt certificates?
Java 是否支持 Let's Encrypt 证书?
Yes. The Let's Encrypt certificate is just a regular public key certificate. Java supports it (according to Let's Encrypt Certificate Compatibility, for Java 7 >= 7u111 and Java 8 >= 8u101).
是的。Let's Encrypt 证书只是一个普通的公钥证书。Java 支持它(根据Let's Encrypt Certificate Compatibility,对于 Java 7 >= 7u111 和 Java 8 >= 8u101)。
Does Java trust Let's Encrypt certificates out of the box?
Java 是否信任 Let's Encrypt 开箱即用的证书?
No / it depends on the JVM. The truststore of Oracle JDK/JRE up to 8u66 contains neither the Let's Encrypt CA specifically nor the IdenTrust CA that cross signed it. new URL("https://letsencrypt.org/").openConnection().connect();
for example results in javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException
.
否/这取决于JVM。高达 8u66 的 Oracle JDK/JRE 信任库既不包含 Let's Encrypt CA,也不包含对其进行交叉签名的 IdenTrust CA。new URL("https://letsencrypt.org/").openConnection().connect();
例如导致javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException
.
You can however provide your own validator / define a custom keystore that contains the required root CA or import the certificate into the JVM truststore.
但是,您可以提供自己的验证器/定义包含所需根 CA 的自定义密钥库或将证书导入 JVM 信任库。
https://community.letsencrypt.org/t/will-the-cross-root-cover-trust-by-the-default-list-in-the-jdk-jre/134/10discusses the topic as well.
Here is some example code that shows how to add a certificate to the default truststore at runtime. You'll just need to add the certificate (exported from firefox as .der and put in classpath)
下面是一些示例代码,展示了如何在运行时将证书添加到默认信任库。您只需要添加证书(从 firefox 导出为 .der 并放入类路径中)
Based on How can I get a list of trusted root certificates in Java?and http://developer.android.com/training/articles/security-ssl.html#UnknownCa
基于如何在 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);
}
}
}
回答by Jan Berkel
I know the OP asked for a solution without local configuration changes, but in case you want to add the trust chain to the keystore permanently:
我知道 OP 要求没有本地配置更改的解决方案,但如果您想将信任链永久添加到密钥库:
$ 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
回答by Anthony O.
For JDK which do not support Let's Encrypt certificates yet, you can add those to the JDK cacerts
following this process (thanks to this).
对于尚不支持 Let's Encrypt 证书的 JDK,您可以cacerts
按照此过程将其添加到 JDK (感谢此)。
Download all the certificates on https://letsencrypt.org/certificates/(choose the derformat) and add them one by one with this kind of command (example for letsencryptauthorityx1.der
):
下载https://letsencrypt.org/certificates/上的所有证书(选择der格式)并使用这种命令(例如 for letsencryptauthorityx1.der
)一一添加:
keytool -import -keystore PATH_TO_JDK\jre\lib\security\cacerts -storepass changeit -noprompt -trustcacerts -alias letsencryptauthorityx1 -file PATH_TO_DOWNLOADS\letsencryptauthorityx1.der
回答by dimalinux
Detailed answer for those of us willing to make local config changes that includes backing up the config file:
我们这些愿意进行本地配置更改(包括备份配置文件)的人的详细答案:
1. Test if it is working before the changes
1.在更改之前测试它是否工作
If you don't have a test program already, you can use my java SSLPing ping program which tests the TLS handshake (will work with any SSL/TLS port, not just HTTPS). I'll use the prebuilt SSLPing.jar, but reading the code and building it yourself is a quick and easy task:
如果您还没有测试程序,您可以使用我的 java SSLPing ping 程序来测试 TLS 握手(适用于任何 SSL/TLS 端口,而不仅仅是 HTTPS)。我将使用预构建的 SSLPing.jar,但阅读代码并自行构建它是一项快速而简单的任务:
$ git clone https://github.com/dimalinux/SSLPing.git
Cloning into 'SSLPing'...
[... output snipped ...]
Since my Java version is earlier than 1.8.0_101 (not released at the time of this writing), a Let's Encrypt certificate will not verify by default. Let's see what failure looks like before applying the fix:
由于我的 Java 版本早于 1.8.0_101(在撰写本文时尚未发布),因此默认情况下不会验证 Let's Encrypt 证书。在应用修复之前,让我们看看失败是什么样的:
$ 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. Import the certificate
2. 导入证书
I'm on Mac OS X with the JAVA_HOME environment variable set. Later commands will assume this variable is set for the java installation you are modifying:
我在 Mac OS X 上使用 JAVA_HOME 环境变量集。稍后的命令将假定为您正在修改的 java 安装设置了此变量:
$ echo $JAVA_HOME
/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/
Make a backup of the cacerts file we will be modifying so you can back out any change without reinstalling the JDK:
备份我们将要修改的 cacerts 文件,这样您就可以在不重新安装 JDK 的情况下取消任何更改:
$ sudo cp -a $JAVA_HOME/jre/lib/security/cacerts $JAVA_HOME/jre/lib/security/cacerts.orig
Download the signing certificate we need to import:
下载我们需要导入的签名证书:
$ wget https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.der
Perform the import:
执行导入:
$ 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. Verify that it is working after the changes
3. 验证更改后是否正常工作
Verify that Java is now happy connecting to the SSL port:
验证 Java 现在是否可以连接到 SSL 端口:
$ java -jar SSLPing/dist/SSLPing.jar helloworld.letsencrypt.org 443
About to connect to 'helloworld.letsencrypt.org' on port 443
Successfully connected