【问题标题】:How can my library with built in ssl certificate also allow use with default certs我的带有内置 ssl 证书的库如何也允许使用默认证书
【发布时间】:2018-10-25 19:36:10
【问题描述】:

我正在为内部客户端分发一个库 jar,该库包含一个证书,用于调用我们网络内部的服务。

信任管理器设置如下

    TrustManagerFactory trustManagerFactory = 
      TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    KeyStore keystore = KeyStore.getInstance("JKS");
    InputStream keystoreStream = 
      clazz.getClassLoader().getResourceAsStream("certs.keystore"); // (on classpath)
    keystore.load(keystoreStream, "pa55w0rd".toCharArray());
    trustManagerFactory.init(keystore);
    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
    SSLContext context = SSLContext.getInstance("SSL");
    context.init(null, trustManagers, null);

    SSLSocketFactory socketFact = context.getSocketFactory();
    connection.setSSLSocketFactory(socketFact);

所有这些都可以正常工作,除非用户需要其他证书或默认证书。

我试过这个 Registering multiple keystores in JVM 没有运气(我无法将其概括为我的案例)

如何在使用我的证书的同时仍然允许用户库也使用他们自己的证书?

【问题讨论】:

  • 您是否尝试过链接帖子中的复合信任管理器?听起来就是这样,你得到了什么错误?
  • '该库包含一个用于调用服务的证书'。您的意思是说服务 使用它并且您的客户需要信任 它吗?这不是一回事。

标签: java ssl ssl-certificate


【解决方案1】:

您正在使用充当信任库(您信任的服务器的证书)的自定义密钥库配置连接。您没有覆盖默认的 JVM 行为,因此包含您的库的其他应用程序可以建立的其余连接不会受到影响。

因此您不需要多个密钥库管理器,事实上,您的代码可以完美运行。

我在下面附上了一个完整的示例,它使用了一个包含 Google 根 CA 的密钥库 google.jks,以及一个使用默认 JVM 信任库的连接。这是输出

request("https://www.google.com/", "test/google.jks", "pa55w0rd"); //OK 
request("https://www.aragon.es/", "test/google.jks", "pa55w0rd");  // FAIL sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
request("https://www.aragon.es/", null, null); //OK

问题不在您附加的代码中,因此请在您的代码中检查以下内容:

  • 信任库certs.keystore 确实在您的类路径中

  • Truststore 设置未使用-Djavax.net.ssl.trustStore在 JVM 级别设置

  • 发现的错误(请在您的问题中包含)确实与 SSL 连接有关


package test;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;


public class HTTPSCustomTruststore {

    public final static void main (String argv[]) throws Exception{
        request("https://www.google.com/", "test/google.jks", "pa55w0rd"); //Expected OK 
        request("https://www.aragon.es/","test/google.jks","pa55w0rd");  // Expected  FAIL
        request("https://www.aragon.es/",null,null); //using default truststore. OK 
    }

    public static void configureCustom(HttpsURLConnection connection, String truststore, String pwd)throws Exception{
        TrustManagerFactory trustManagerFactory = 
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        KeyStore keystore = KeyStore.getInstance("JKS");
        InputStream keystoreStream = HTTPSCustomTruststore.class.getClassLoader().getResourceAsStream(truststore);
        keystore.load(keystoreStream, pwd.toCharArray());
        trustManagerFactory.init(keystore);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        SSLContext context = SSLContext.getInstance("SSL");
        context.init(null, trustManagers,  new java.security.SecureRandom());

        SSLSocketFactory socketFact = context.getSocketFactory();
        connection.setSSLSocketFactory(socketFact);
    }


    public static void request(String urlS, String truststore, String pwd) {
        try {
            URL url = new URL(urlS);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            if (truststore != null) {
                configureCustom((HttpsURLConnection) conn, truststore, pwd);
            }   
            conn.connect();

            int statusCode = conn.getResponseCode();
            if (statusCode != 200) {
                System.out.println(urlS + " FAIL");
            } else {
                System.out.println(urlS + " OK");
            }
        } catch (Exception e) {
            System.out.println(urlS + " FAIL " + e.getMessage());
        }
    }
}

【讨论】:

  • 我没有机会尝试这个,但我接受了答案,所以赏金不会过期。放假后会回来的
【解决方案2】:

您可以将默认证书导入到您的自定义存储中,以拥有一个组合的自定义存储并使用它。

【讨论】:

  • 这在 JRE 升级后无法生存。如果标准 cacerts 发生更改,此客户端将永远不会信任新的 CA,并将继续信任任何被删除的 CA。
  • 正确,您需要自动化该过程,使其成为构建的一部分,因此它始终是最新的。
  • ... 并在每次有 JDK 更新时重新部署它。让您忙于做根本不应该做的事情。
  • 如果是自动化的,那就不是“忙”了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-21
  • 2022-12-09
  • 2016-06-20
  • 1970-01-01
  • 2012-10-13
  • 2012-02-10
  • 1970-01-01
相关资源
最近更新 更多