我正在用Java编写一个应用程序,该应用程序通过HTTPS连接到两个Web服务器。一个通过默认的信任链获得了受信任的证书,另一个使用自签名证书。当然,连接到第一个服务器是开箱即用的,而使用自签名证书连接到服务器是行不通的,直到我使用该服务器上的证书创建了一个trustStore。但是,默认情况下,到默认受信任服务器的连接不再起作用,因为一旦创建了自己的默认信任库,显然就会被忽略。

我发现的一种解决方案是将默认trustStore中的证书添加到我自己的证书中。但是,我不喜欢这种解决方案,因为它要求我继续管理该trustStore。 (我不能假设这些证书在可预见的将来保持不变,对吗?)

除此之外,我发现了两个存在5年的类似问题的线程:

Registering multiple keystores in JVM

How can I have multiple SSL certificates for a Java server

他们俩都深入Java SSL基础架构。我希望到目前为止,有一个更方便的解决方案,可以在我的代码的安全性检查中轻松解释。

最佳答案

您可以使用类似于previous answer中提到的模式(针对另一个问题)。

本质上,掌握默认的信任管理器,创建另一个使用您自己的信任库的信任管理器。将它们都包装在一个自定义的信任管理器实现中,该实现将调用都委派给两者(当一个失败时回退到另一个)。

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);

您不必将该上下文设置为默认上下文。如何使用它取决于您使用的客户端库(以及从中获取套接字工厂的位置)。

这就是说,原则上,无论如何,您始终必须根据需要更新信任库。 Java 7 JSSE引用指南对此有一个“重要说明”,现在降级为"note" in version 8 of the same guide:

关于java - 在Java和默认的信任库中使用自定义信任库,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24555890/

10-12 01:25