以编程方式访问 java 密钥库以创建 SSLSocketFactory

Posted

技术标签:

【中文标题】以编程方式访问 java 密钥库以创建 SSLSocketFactory【英文标题】:Access java keystore programmatically to create SSLSocketFactory 【发布时间】:2014-01-27 04:09:58 【问题描述】:

我正在与启用 ssl 的服务器建立 SSL 连接。我的硬件文件系统 java 密钥库中有一个 cacerts 文件,我使用 keytool 从中提取了证书,并且我正在提供此证书文件以创建 SSLSocketfactory 以建立ssl 连接,与下面的代码 sn-p 配合得很好。

我想知道如何直接访问 cacerts (java keystore) 文件,然后选择证书并建立 ssl 连接。现在,我正在使用我的 jar 文件将提取的证书打包到类路径中,这不是一个好习惯,因为我希望从密钥库中加载它。

下面是我目前如何创建 SSLSocketFactory 的工作代码 sn-p。

private SSLSocketFactory createSSLFactory() 
  KeyStore keyStore = null;
  TrustManagerFactory tmf = null;
  SSLContext ctx = null;

  try 
    keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    InputStream is = null;
    is = SSLConnection.class.getResourceAsStream("/" + "my-keystore");
    keyStore.load(is, "changeit".toCharArray());
    tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(keyStore);
    ctx = SSLContext.getInstance("TLSv1");
    ctx.init(null, tmf.getTrustManagers(), null);
    SSLSocketFactory factory = ctx.getSocketFactory();
    return factory;
   catch (Exception e) 
    // exception handling
  
  return null;

【问题讨论】:

为什么?您甚至不需要此代码,更不用说更多代码了。只需设置系统属性即可。 会尝试回发,只是好奇。如果通过系统属性设置密钥库路径,它会像那样遍历密钥库并为服务器选择正确的证书吗? 你有它回到前面。它遍历信任库以查找 trusted 证书;在 SSL 握手的第一条消息中将这些 subjectDNs 作为受信任的 CA 发送到服务器;服务器发回应该由这些 CA 之一签名的证书;然后客户端在密钥库中检查。没有“为服务器选择正确的证书”的问题,除非您仍在谈论客户端身份验证。 【参考方案1】:

在私钥和认证证书的情况下,将 KeyStore 嵌入到 JAR 文件中没有任何意义。客户端证书应该唯一标识客户端。它是主机的属性,而不是 JAR 文件,可以无限复制。允许多个客户端使用相同的客户端证书是没有意义的。这是对 PKI 的滥用。

【讨论】:

我的错误,它不是客户端身份验证,没有客户端到服务器的唯一标识。我将编辑问题。它只是建立到服务器的ssl。服务器的公钥给出连接到它。 这仍然是不好的做法。您应该使用 JDK 附带的信任库。如果您这样做是因为您的服务器使用的是自签名证书,那么正确的解决方案是不要这样做。得到它的签名。你会比在这里花的钱多。【参考方案2】:

您可以将密钥库(和信任库)作为系统属性传递给 JVM。见这里:https://***.com/a/882479/131929

-Djavax.net.ssl.keyStoreType=pkcs12
-Djavax.net.ssl.trustStoreType=jks
-Djavax.net.ssl.keyStore=clientcertificate.p12
-Djavax.net.ssl.trustStore=gridserver.keystore
-Djavax.net.debug=ssl # very verbose debug
-Djavax.net.ssl.keyStorePassword=$PASS
-Djavax.net.ssl.trustStorePassword=$PASS

那你就可以了

URL url = new URL("https://someurl");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
InputStream inputstream = conn.getInputStream();

【讨论】:

您甚至不需要获取SSLSocketFactory 并将其设置为默认值。默认值已经是默认值了。正如 OP 现在所说他不需要客户端身份验证,他不需要 keyStore/keyStorePassword, 只是 trustStore. trustStore 它是,与 trustStore 和 keystore 混淆了。做了一些学习,一切顺利。 @EJP - 嗯,当然是的,抱歉,我混合了我拥有的两个代码示例(现已修复)。我也认为OP的问题很奇怪。从问题直接回答。如果问题是“错误的”,那会让你无处可去……【参考方案3】:

您需要添加信任管理器:

            SSLSocketFactory factory = null;
        try 
            SSLContext ctx;
            KeyManagerFactory kmf;
            TrustManagerFactory tmf;
            KeyStore ks;
            char[] passphrase = "passphrase".toCharArray();

            ctx = SSLContext.getInstance("TLS");
            kmf = KeyManagerFactory.getInstance("SunX509");
            tmf = TrustManagerFactory.getInstance("SunX509");
            ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("testkeys"), passphrase);

            kmf.init(ks, passphrase);
            tmf.init(ks);
            ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

            factory = ctx.getSocketFactory();
         catch (Exception e) 
            throw new IOException(e.getMessage());
        

        SSLSocket socket = (SSLSocket)factory.createSocket(host, port);

【讨论】:

以上是关于以编程方式访问 java 密钥库以创建 SSLSocketFactory的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式获取 Office 2013 产品密钥

以编程方式生成 SSH 密钥对(c# windows)

Android - 以无代码方式集成您的库以显示视图

Java:通过代码生成时,密钥库格式无效

尝试访问以编程方式创建的 <iframe> 的文档对象时出现“访问被拒绝”JavaScript 错误(仅限 IE)

访问 iPod 库以使用 AVAudioPlayer 或使用 AVPlayer 远程控制事件