首页 文章

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

提问于
浏览
43

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

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

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

Registering multiple keystores in JVM

How can I have multiple SSL certificates for a Java server

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

2 回答

  • 50

    正如我所知,您还可以使用Apache HttpComponents库中的 SSLContextBuilder 类将自定义密钥库添加到 SSLContext

    SSLContextBuilder builder = new SSLContextBuilder();
    try {
         keyStore.load(null, null);
         builder.loadTrustMaterial(keyStore, null);
         builder.loadKeyMaterial(keyStore, null);
    } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException
              | UnrecoverableKeyException e) {
         log.error("Can not load keys from keystore '{}'", keyStore.getProvider(), e);
    }
    return builder.build();
    
  • 0

    您可以使用类似于我在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参考指南有一个关于此的"important note",现在降级为"note" in version 8 of the same guide

    JDK在java-home / lib / security / cacerts文件中附带有限数量的受信任根证书 . 如keytool参考页中所述,如果您将此文件用作信任库,则您有责任维护(即添加和删除)此文件中包含的证书 . 根据您联系的服务器的证书配置,您可能需要添加其他根证书 . 从相应的供应商处获取所需的特定根证书 .

相关问题