Java SSL连接,以编程方式将服务器证书添加到 keystore

标签 java ssl

我正在将 SSL 客户端连接到我的 SSL 服务器。 当客户端由于客户端的 keystore 中不存在根而无法验证证书时,我需要选择将该证书添加到代码中的本地 keystore 并继续。 有一些始终接受所有证书的示例,但我希望用户在不离开应用程序的情况下验证证书并将其添加到本地 keystore 。

SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("localhost", 23467);
try{
    sslsocket.startHandshake();
} catch (IOException e) {
    //here I want to get the peer's certificate, conditionally add to local key store, then reauthenticate successfully
}

有很多关于自定义 SocketFactory、TrustManager、SSLContext 等的东西,我真的不明白它们是如何组合在一起的,或者哪一条是实现我目标的最短路径。

最佳答案

您可以使用 X509TrustManager 来实现它.

使用

获取 SSLContext
SSLContext ctx = SSLContext.getInstance("TLS");

然后使用您的自定义 X509TrustManager 通过使用 SSLContext#init 对其进行初始化. SecureRandom 和 KeyManager[] 可能为空。后者仅在您执行客户端身份验证时有用,如果在您的场景中只有服务器需要进行身份验证,您不需要设置它。

从此 SSLContext 中,使用 SSLContext#getSocketFactory 获取您的 SSLSocketFactory并按计划进行。

就您的 X509TrustManager 实现而言,它可能如下所示:

TrustManager tm = new X509TrustManager() {
    public void checkClientTrusted(X509Certificate[] chain,
                    String authType)
                    throws CertificateException {
        //do nothing, you're the client
    }

    public X509Certificate[] getAcceptedIssuers() {
        //also only relevant for servers
    }

    public void checkServerTrusted(X509Certificate[] chain,
                    String authType)
                    throws CertificateException {
        /* chain[chain.length -1] is the candidate for the
         * root certificate. 
         * Look it up to see whether it's in your list.
         * If not, ask the user for permission to add it.
         * If not granted, reject.
         * Validate the chain using CertPathValidator and 
         * your list of trusted roots.
         */
    }
};

编辑:

Ryan 是对的,我忘了解释如何将新根添加到现有根中。假设您当前的受信任根 KeyStore 来自 cacerts(JDK 附带的“Java 默认信任库”,位于 jre/lib/security 下)。我假设您使用 KeyStore#load(InputStream, char[]) 加载了该 keystore (它是 JKS 格式)。

KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream in = new FileInputStream("<path to cacerts"");
ks.load(in, "changeit".toCharArray);

cacerts 的默认密码是“changeit”,如果您还没有更改的话。

然后您可以使用 KeyStore#setEntry 添加其他受信任的根.您可以省略 ProtectionParameter(即 null),KeyStore.Entry 将是 TrustedCertificateEntry它将新根作为其构造函数的参数。

KeyStore.Entry newEntry = new KeyStore.TrustedCertificateEntry(newRoot);
ks.setEntry("someAlias", newEntry, null);

如果您希望在某个时候保留更改后的信任存储,您可以使用 KeyStore#store(OutputStream, char[] 来实现此目的。 .

关于Java SSL连接,以编程方式将服务器证书添加到 keystore ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6755180/

相关文章:

Java - 小数除以大数

android - 使用 XA_HTTP_CLIENT_HANDLER_TYPE 在 AndroidEnvironment 设置中部署应用程序

android - 如何使用未知 CA 自签名证书让 Android Volley 执行 HTTPS 请求?

java - 如何为每个循环填充 ArrayList? ( java )

Java 调试器查找实例化对象的位置

java - org.openqa.selenium.WebDriverException : Timed out waiting for driver server to start. 构建信息 : version: 'unknown' , 修订版: 'unknown'

python - 请求 : what is the difference between cert and verify?

ssl - 配置错误 : Elastic Load Balancer + EC2 + Route 53

php - SSL key 解密

java - 嵌入式 Kafka 以错误数量的分区开始