以编程方式将证书添加到 Java KeyStore 的选项
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24043397/
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
Options for Programmatically Adding Certificates to Java KeyStore
提问by IcedDante
I was getting an SSL Handshake Exception error: PKIX "path does not chain" (described here). I fixed it by importing a certificate chain using openssl:
我收到 SSL 握手异常错误:PKIX“路径未链接”(此处描述)。我通过使用 openssl 导入证书链来修复它:
openssl s_client -host www.envmgr.com -port 443 -showcerts > cert_chain.crt
openssl s_client -host www.envmgr.com -port 443 -showcerts > cert_chain.crt
and installed it into my JDK's keystore:
并将其安装到我的 JDK 的密钥库中:
keytool -import -alias envmgrchain -file cert_chain.crt -keystore cacerts -storepass changeit
Well this works. Hooray. The problem is we'll be putting our application up on a cloud server like rackspace or AWS and I think there is a good chance that we won't have access to modify the keystore of the JVM to add this chain.
嗯,这有效。万岁。问题是我们将把我们的应用程序放在像 rackspace 或 AWS 这样的云服务器上,我认为我们很有可能无法修改 JVM 的密钥库来添加这个链。
I thought, "no problem, I'll just add this certificate chain to the keystore programmatically" so I removed it from my JVM:
我想,“没问题,我会以编程方式将此证书链添加到密钥库中”,所以我从我的 JVM 中删除了它:
keytool -delete -alias envmgrchain -keystore cacerts -storepass changeit
and added this code:
并添加了以下代码:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
//Create an empty keystore that we can load certificate into
trustStore.load(null);
InputStream fis = new FileInputStream("cert_chain.crt");
BufferedInputStream bis = new BufferedInputStream(fis);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while(bis.available()>0) {
Collection<? extends Certificate> certs = cf.generateCertificates(bis);
Iterator<? extends Certificate> iter = certs.iterator();
//Add each cert in the chain one at a time
for(int i=0; i<certs.size(); i++) {
Certificate cert = iter.next();
String alias = "chaincert"+((i>0)?i:"");
trustStore.setCertificateEntry(alias, cert);
}
}
bis.close();
fis.close();
//Add custom keystore to TrustManager
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
SSLContext ctx = SSLContext.getInstance("TLSv1");
ctx.init(null, tmf.getTrustManagers(), null);
But when I run it, the PKIX error reappears. Is the above code not equivalent to keytool -import? I feel like I'm either adding certificates to the KeyStore incorrectly, or I'm not installing the Keystore into the TrustManager in the right way.
但是当我运行它时,PKIX 错误再次出现。上面的代码不等价于 keytool -import 吗?我觉得我要么错误地将证书添加到 KeyStore,要么我没有以正确的方式将 Keystore 安装到 TrustManager 中。
FYI: I am also attempting to address this issue by implementing an X509TrustManager.
仅供参考:我也试图通过实施 X509TrustManager 来解决这个问题。
采纳答案by jww
Here's code you can use for clients to programatically add your CA at runtime. You don't need to put it in any store - just carry around the PEM encoded file. You can even hard code it into your program so there's no separate file to manage.
以下代码可用于客户端在运行时以编程方式添加 CA。您无需将其放在任何商店中 - 只需随身携带 PEM 编码文件即可。您甚至可以将其硬编码到您的程序中,因此无需管理单独的文件。
static String CA_FILE = "ca-cert.pem";
...
FileInputStream fis = new FileInputStream(CA_FILE);
X509Certificate ca = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(new BufferedInputStream(fis));
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry(Integer.toString(1), ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
...
You will need a trusted distribution channel to ensure your program is not modified while sitting on the server waiting to be picked or while traveling down the wire while being installed.
您将需要一个受信任的分发渠道,以确保您的程序在等待被挑选的服务器上或在安装时沿着线路传输时不会被修改。
openssl s_client -host www.envmgr.com -port 443 -showcerts > cert_chain.crt
openssl s_client -host www.envmgr.com -port 443 -showcerts > cert_chain.crt
You should only need to trust the root certificate, and not the entire chain. The server is responsible for sending all intermediate certificates required to build the chain. If the server is not sending all intermediate certificates required to build the chain, then the server is misconfigured.
您应该只需要信任根证书,而不是整个链。服务器负责发送构建链所需的所有中间证书。如果服务器没有发送构建链所需的所有中间证书,则服务器配置错误。
The problem you are experiencing is called the "Which Directory" problem. Its a well known problem in PKI. Essentially, it means a client does not know where to go to fetch a missing intermediate certificate. You solve it by having the server send all required intermediates along with the server's certifcate. See OWASP's TLS Cheatsheet and Rule - Always Provide All Needed Certificates.
您遇到的问题称为“哪个目录”问题。它是 PKI 中的一个众所周知的问题。从本质上讲,这意味着客户端不知道去哪里获取丢失的中间证书。您可以通过让服务器发送所有必需的中间件以及服务器的证书来解决它。请参阅 OWASP 的 TLS 备忘单和规则 - 始终提供所有需要的证书。
Just bike shedding, but there's a whole nother can of worms here with Java (especially Java 7 and lower):
只是自行车脱落,但这里有一个完整的 Java 蠕虫(尤其是 Java 7 及更低版本):
SSLContext ctx = SSLContext.getInstance("TLSv1");
ctx.init(null, tmf.getTrustManagers(), null);
You can improve upon it, if desired. See SSLSocketFactoryEx
at Which Cipher Suites to enable for SSL Socket?. It closes some gaps in protocol versions, cipher suites, etc provided by default in Java SSLSocketFactory
.
如果需要,您可以改进它。见SSLSocketFactoryEx
在哪个密码套件启用SSL套接字?. 它弥补了 Java 中默认提供的协议版本、密码套件等方面的一些差距SSLSocketFactory
。
回答by user207421
Don't. You shouldn't be modifying files that come with the JRE. Next upgrade, your updates are gone. You should ship your owntruststore, built from the one that comes with the JRE pluswhatever extra certificates you want to trust. This is part of your application build.
别。您不应该修改 JRE 附带的文件。下一次升级,您的更新将消失。您应该发布自己的信任库,该信任库由 JRE 附带的信任库以及您想要信任的任何额外证书构建而成。这是您的应用程序构建的一部分。
If you then want to modify your own truststore at runtime, go ahead, but then you need to be aware that the JVM won't necessarily see the changes until it's restarted: it certainly won't see them within the same SSLContext
that you use to obtain the certificates you want to add.
如果您想在运行时修改您自己的信任库,请继续,但是您需要注意 JVM 在重新启动之前不一定会看到更改:它肯定不会在SSLContext
您使用的相同位置看到它们获取要添加的证书。