我收到了SSL握手异常错误:PKIX"路径没有链接" (described here)。 我通过使用openssl导入证书链来修复它:
openssl s_client -host www.envmgr.com -port 443 -showcerts > cert_chain.crt
并将其安装到我的JDK密钥库中:
keytool -import -alias envmgrchain -file cert_chain.crt -keystore cacerts -storepass changeit
这很有效。万岁。问题是我们将应用程序放在像rackspace或AWS这样的云服务器上,我认为我们很有可能无法修改JVM的密钥库以添加此链。
我想,"没问题,我只是以编程方式将此证书链添加到密钥库中"所以我从JVM中删除了它:
keytool -delete -alias envmgrchain -keystore cacerts -storepass changeit
并添加了此代码:
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);
但是当我运行它时,PKIX错误再次出现。上面的代码不等同于keytool -import吗?我觉得我不正确地向KeyStore添加证书,或者我没有以正确的方式将Keystore安装到TrustManager中。
仅供参考:我也试图通过实施X509TrustManager来解决这个问题。
答案 0 :(得分:17)
您可以使用以下代码在客户端以编程方式在运行时添加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);
...
您需要一个受信任的分发渠道,以确保您的程序在等待挑选的服务器上或在安装时沿着网络传输时不会被修改。
openssl s_client -host www.envmgr.com -port 443 -showcerts&gt; cert_chain.crt
您应该只需要信任根证书,而不是整个链。服务器负责发送构建链所需的所有中间证书。如果服务器没有发送构建链所需的所有中间证书,则服务器配置错误。
您遇到的问题称为“哪个目录”问题。它是PKI中众所周知的问题。从本质上讲,这意味着客户端不知道从哪里获取缺少的中间证书。您可以通过让服务器发送所有必需的中间产品以及服务器的证书来解决它。请参阅OWASP的TLS Cheatsheet和Rule - Always Provide All Needed Certificates。
只是自行车脱落,但是Java中存在一整套蠕虫(特别是Java 7及更低版本):
SSLContext ctx = SSLContext.getInstance("TLSv1");
ctx.init(null, tmf.getTrustManagers(), null);
如果需要,您可以改进它。请参阅Which Cipher Suites to enable for SSL Socket?上的SSLSocketFactoryEx
。它弥补了Java SSLSocketFactory
中默认提供的协议版本,密码套件等方面的一些空白。
答案 1 :(得分:0)
唐&#39;吨。您不应该修改JRE附带的文件。下次升级,您的更新消失了。您应该发送自己的信任库,该信任库是从JRE 附带附带的信任库构建的,您想要信任哪些额外的证书。这是应用程序构建的一部分。
如果您想在运行时修改自己的信任库,请继续,但是您需要知道JVM在重新启动之前不会看到更改:它肯定会赢得&#39 ;在用于获取要添加的证书的SSLContext
内查看它们。