在 java 中使用自定义信任库以及默认信任库

新手上路,请多包涵

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

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

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

在 JVM 中注册多个密钥库

我怎样才能为 Java 服务器拥有多个 SSL 证书

它们都深入到 Java SSL 基础结构。我希望现在有一个更方便的解决方案,我可以在代码的安全审查中轻松解释。

原文由 user1785730 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.4k
2 个回答

您可以使用与我在 之前的回答 中提到的类似的模式(针对不同的问题)。

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

 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 参考指南对此有一个“重要说明”,现在 在同一指南的版本 8 中降级为一个“说明”

JDK 在 java-home/lib/security/cacerts 文件中附带了有限数量的可信根证书。如 keytool 参考页中所述,如果您将此文件用作信任库,则您有责任维护(即添加和删除)此文件中包含的证书。

根据您联系的服务器的证书配置,您可能需要添加其他根证书。从适当的供应商处获取所需的特定根证书。

原文由 Bruno 发布,翻译遵循 CC BY-SA 3.0 许可协议

也许我来不及回答这个问题 6 年了,但它也可能对其他开发人员有所帮助。我也遇到了加载默认信任库和我自己的自定义信任库的相同挑战。在为多个项目使用相同的自定义解决方案后,我认为创建一个库并公开它以回馈社区会很方便。请在这里查看: Github - SSLContext-Kickstart

用法:

 import nl.altindag.ssl.SSLFactory;

import javax.net.ssl.SSLContext;
import java.security.cert.X509Certificate;
import java.nio.file.Path;
import java.util.List;

public class App {

    public static void main(String[] args) {
        Path trustStorePath = ...;
        char[] password = "password".toCharArray();

        SSLFactory sslFactory = SSLFactory.builder()
                .withDefaultTrustMaterial()
                .withTrustMaterial(trustStorePath, password)
                .build();

        SSLContext sslContext = sslFactory.getSslContext();
        List<X509Certificate> trustedCertificates = sslFactory.getTrustedCertificates();
    }

}

我不太确定我是否应该在这里发布它,因为它也可以被视为一种推广“我的图书馆”的方式,但我认为它可能对面临同样挑战的开发人员有所帮助。

您可以使用以下代码片段添加依赖项:

 <dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart</artifactId>
    <version>7.4.4</version>
</dependency>

原文由 Hakan54 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题