如何使用多个信任源初始化TrustManagerFactory?

var*_*unr 9 java ssl jsse x509

我的应用程序有一个个人密钥库,其中包含用于本地网络的可信自签名证书 - 比如说mykeystore.jks.我希望能够使用已在本地配置的自签名证书连接到公共站点(例如google.com)以及本地网络中的站点.

这里的问题是,当我连接到https://google.com时,路径构建失败,因为设置我自己的密钥库会覆盖包含与JRE捆绑在一起的根CA的默认密钥库,报告异常

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Run Code Online (Sandbox Code Playgroud)

但是,如果我将CA证书导入我自己的密钥库(mykeystore.jks),它可以正常工作.有没有办法支持两者?

我有自己的TrustManger用于此目的,

public class CustomX509TrustManager implements X509TrustManager {

        X509TrustManager defaultTrustManager;

        public MyX509TrustManager(KeyStore keystore) {
                TrustManagerFactory trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                trustMgrFactory.init(keystore);
                TrustManager trustManagers[] = trustMgrFactory.getTrustManagers();
                for (int i = 0; i < trustManagers.length; i++) {
                    if (trustManagers[i] instanceof X509TrustManager) {
                        defaultTrustManager = (X509TrustManager) trustManagers[i];
                        return;
                    }
                }

        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            try {
                defaultTrustManager.checkServerTrusted(chain, authType);
            } catch (CertificateException ce) {
            /* Handle untrusted certificates */
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后我初始化SSLContext,

TrustManager[] trustManagers =
            new TrustManager[] { new CustomX509TrustManager(keystore) };
SSLContext customSSLContext =
        SSLContext.getInstance("TLS");
customSSLContext.init(null, trustManagers, null);
Run Code Online (Sandbox Code Playgroud)

并设置套接字工厂,

HttpsURLConnection.setDefaultSSLSocketFactory(customSSLContext.getSocketFactory());
Run Code Online (Sandbox Code Playgroud)

主程序,

URL targetServer = new URL(url);
HttpsURLConnection conn = (HttpsURLConnection) targetServer.openConnection();
Run Code Online (Sandbox Code Playgroud)

如果我没有设置我自己的信任管理员,那就很好地连接到https://google.com.如何获得指向默认密钥库的"默认信任管理器"?

Pas*_*asi 16

trustMgrFactory.init(keystore);您使用自己的个人密钥库配置defaultTrustManager时,而不是系统默认密钥库.

基于阅读sun.security.ssl.TrustManagerFactoryImpl的源代码,它看起来就像trustMgrFactory.init((KeyStore) null);你需要的那样(加载系统默认密钥库),并且基于快速测试,它似乎对我有用.

  • 但这不允许您在自定义密钥库中使用您的证书,只能使用系统上安装的 CA 证书。 (2认同)

Hug*_*ner 10

这里的答案是我如何理解如何做到这一点.如果您只想接受系统CA证书以及证书的自定义密钥库,我将其简化为具有一些便捷方法的单个类.完整代码在这里:

https://gist.github.com/HughJeffner/6eac419b18c6001aeadb

KeyStore keystore; // Get your own keystore here
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] tm = CompositeX509TrustManager.getTrustManagers(keystore);
sslContext.init(null, tm, null);
Run Code Online (Sandbox Code Playgroud)

  • 复合信任管理器是一个非常有用的解决方 (2认同)