【问题标题】:Selecting specific client certificate from keystore using SAAJ使用 SAAJ 从密钥库中选择特定客户端证书
【发布时间】:2026-01-04 06:55:01
【问题描述】:

我在 SAAJ 中编写了一个 Java 客户端,它在通过 HTTP 发送 SOAP 消息时可以成功运行,但是当我尝试通过 HTTPS 将任何 SOAP 消息发送到需要客户端证书的 Web 服务时,它不起作用。

在以下链接的页面底部 - SAAJ Security - 声明如下:

在 SAAJ 方面,您需要做的就是使用带有 HTTPS 作为协议的 URL。这只有在证书成功导入到 /jre/lib/security/cacerts 时才有效;否则 JSSE 将不允许连接

我按照上面的指示将客户端证书和相关的根证书导入我的 Java cacerts 并运行程序,但是我收到以下错误:

java.net.SocketException: Connection reset

我对流量进行了 Wireshark 跟踪,发现当服务器要求 Java 代码提供客户端证书时,我没有提供客户端证书,因此我有以下问题:

1) 通过仅将 HTTPS URL 传递给 soapConnection.call() 方法以及将证书导入我的 cacerts 文件,这是否足以进行身份​​验证,即 SAAJ 会自动处理吗?或者是否需要更多上述链接中未描述的步骤?

2) 通过将证书导入到我的 JAVA_HOME 中的 cacerts 文件中,Java SAAJ 客户端在调用soapConnection.call() 时是否自动知道要查看此处?还是我需要明确告诉我的代码使用什么 cacerts 文件?

3) 如果 Java SAAJ 客户端自动使用我的 JAVA_HOME 下的 cacerts 文件,那么它如何知道要使用哪个客户端证书?再说一次,我不应该显式地编写代码还是 SAAJ 会自动处理这个?

提前致谢

【问题讨论】:

    标签: java http ssl saaj


    【解决方案1】:

    我设法弄明白了。我使用了以下代码:

    static public void doTrustToCertificates() throws Exception 
    {
    
        // Set truststore that contains root / intermediary certs
        System.setProperty("javax.net.ssl.trustStore", "C:\\cert\\trusted.jks");
        System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
    
        // Set keystore that contains private key
        File pKeyFile = new File("C:\\cert\\privatekey.pfx");
        String pKeyPassword = "Password01";
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        InputStream keyInput = new FileInputStream(pKeyFile);
        keyStore.load(keyInput, pKeyPassword.toCharArray());
        keyInput.close();
        keyManagerFactory.init(keyStore, pKeyPassword.toCharArray());
    
        // Set ssl context with private key and truststore details
        SSLContext sc = SSLContext.getInstance("TLSv1");
        sc.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
        SSLSocketFactory sockFact = sc.getSocketFactory();
    
        // Add ssl context to https connection
        HttpsURLConnection.setDefaultSSLSocketFactory(sockFact);
    
    }
    

    然后在 SAAJ 的 soapConnection.call() 方法之前调用 doTrustToCertificates() 方法,它的工作方式如下:

    doTrustToCertificates();
    SOAPMessage soapResponse = soapConnection.call(soapMsgXml, ENDPOINT_URL);
    

    【讨论】:

      【解决方案2】:

      你没有得到这样的指导。这些说明具有误导性,但它们仅适用于服务器证书自签名的情况,根本不适用于客户端需要证书的情况。

      在这种情况下,您需要在客户端的keystore 中创建客户端证书。您可以创建一个 CSR 并对其进行签名,然后使用您在生成密钥对和 CSR 时开始使用的相同别名将其导入回同一密钥库,或者您需要生成一个自签名证书 (yuck) 并将其导出并将其导入服务器的信任库。

      您无需编写任何代码。您需要做的就是设置系统属性javax.net.ssl.keyStore 和朋友告诉JSSE 您的客户端密钥库。

      【讨论】:

        最近更新 更多