问题描述
我正在用Java编写一个应用程序,它通过HTTPS连接到两个Web服务器。一个通过默认信任链获得证书,另一个使用自签名证书。当然,连接到第一台服务器是开箱即用的,而连接到具有自签名证书的服务器在我使用该服务器的证书创建一个trustStore之前不起作用。但是,与默认值受信任的服务器的连接不再起作用,因为显然我创建自己的默认trustStore会被忽略。
I'm writing an application in Java which connects to two web servers via HTTPS. One got a certificate trusted via the default chain of trust, the other uses a self signed certificate. Of course, connecting to the first server worked out of the box, whereas connecting to the server with the self signed certificate did not work until I created a trustStore with the certificate from that server. However, the connection to the by default trusted server does not work any more, because apparently the default trustStore gets to be ignored once I created my own.
我发现一个解决方案是将证书从默认的trustStore添加到我自己的。但是,我不喜欢这个解决方案,因为它需要我继续管理那个trustStore。 (我不能假设这些证书在可预见的将来仍然是静态的,对吗?)
One solution I found was to add the certificates from the default trustStore to my own. However, I don't like this solution, because it requires me to keep managing that trustStore. (I cannot assume these certificates remain static in the foreseeable future, right?)
除此之外我发现了两个有着类似问题的5岁线程:
Apart from that I found two 5 year old threads with a similar problem:
它们都深入到Java SSL基础架构中。我希望到现在有一个更方便的解决方案,我可以在我的代码的安全审查中轻松解释。
They both go deep into the Java SSL infrastructure. I was hoping that by now there is a more convenient solution which I can explain easily in a security review of my code.
推荐答案
您可以使用与我在中提到的类似模式(针对其他问题)。
You could use a similar pattern to what I've mentioned in a previous answer (for a different problem).
基本上,获取默认信任管理器,创建第二个使用您自己的信任库的信任管理器。将它们包装在一个自定义信任管理器实现中,该实现委托两者调用(当一个失败时返回另一个)。
Essentially, get hold of the default trust manager, create a second trust manager that uses your own trust store. Wrap them both in a custom trust manager implementation that delegates call to both (falling back on the other when one fails).
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// Using null here initialises the TMF with the default trust store.
tmf.init((KeyStore) null);
// Get hold of the default trust manager
X509TrustManager defaultTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
defaultTm = (X509TrustManager) tm;
break;
}
}
FileInputStream myKeys = new FileInputStream("truststore.jks");
// Do the same with your trust store this time
// Adapt how you load the keystore to your needs
KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
myTrustStore.load(myKeys, "password".toCharArray());
myKeys.close();
tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(myTrustStore);
// Get hold of the default trust manager
X509TrustManager myTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
myTm = (X509TrustManager) tm;
break;
}
}
// Wrap it in your own class.
final X509TrustManager finalDefaultTm = defaultTm;
final X509TrustManager finalMyTm = myTm;
X509TrustManager customTm = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
// If you're planning to use client-cert auth,
// merge results from "defaultTm" and "myTm".
return finalDefaultTm.getAcceptedIssuers();
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
try {
finalMyTm.checkServerTrusted(chain, authType);
} catch (CertificateException e) {
// This will throw another CertificateException if this fails too.
finalDefaultTm.checkServerTrusted(chain, authType);
}
}
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
// If you're planning to use client-cert auth,
// do the same as checking the server.
finalDefaultTm.checkClientTrusted(chain, authType);
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { customTm }, null);
// You don't have to set this as the default context,
// it depends on the library you're using.
SSLContext.setDefault(sslContext);
您不必将该上下文设置为默认上下文。你如何使用它取决于你正在使用的客户端库(以及它从哪里得到它的套接字工厂)。
You don't have to set that context as the default context. How you use it depends on the client library you're using (and where it gets its socket factories from).
原则上说,无论如何,您总是必须根据需要更新信任库。 Java 7 JSSE参考指南有一个关于此的重要说明,现在降级为:
This being said, in principle, you'd always have to update the truststore as required anyway. The Java 7 JSSE Reference Guide had an "important note" about this, now downgraded to just a "note" in version 8 of the same guide:
根据您与
联系的服务器的证书配置,您可能需要添加其他根证书。从相应的供应商处获取
所需的特定根证书。
Depending on the certificate configuration of the servers that you contact, you may need to add additional root certificates. Obtain the needed specific root certificates from the appropriate vendor.
这篇关于在java中使用自定义信任库以及默认信任库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!