【问题标题】:How to use X509 certificates for POST request over HttpsURLConnection?如何使用 X509 证书通过 HttpsURLConnection 进行 POST 请求?
【发布时间】:2014-01-04 19:32:14
【问题描述】:

我有

  1. 自签名服务器证书(来自我需要与之通信的第三方组织)
  2. 我的客户端证书,包含密钥,由该服务器证书签名。

现在我需要使用这些证书通过 HTTPS 发送 POST 请求。 在浏览器中安装它们后,我设法在 Internet Explorer 中通过 https 测试连接:

  1. 服务器证书 - 进入受信任的 CA
  2. 客户证书 - 进入个人证书。

到目前为止,在 java 中,我使用了 neu242 的答案中SO: Java client certificates over HTTPS/SSL 中给出的代码,即接受任何证书。但现在服务器端确实接受了这一点,即 SSL 握手失败。

感谢SO: X509TrustManager Override without allowing ALL certs? 我试图返回getAcceptedIssuers 中的服务器证书,但没有成功。它抛出

javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure

就在getAcceptedIssuers 返回之后。

public X509Certificate[] getAcceptedIssuers() {
    try {
        X509Certificate scert;
        try (InputStream inStream = new FileInputStream("..\\server.crt")) {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            scert = (X509Certificate) cf.generateCertificate(inStream);
        }                        
        return new X509Certificate[]{scert};
    } catch (Exception ex) {
        writeLogFile(ex.getMessage());
        return new X509Certificate[]{};
    }
}

我想我应该以某种方式指定客户端证书,但找不到任何方法。 当然,我可能错了。

希望有人能指引我正确的方向。

【问题讨论】:

  • 握手失败是在客户端还是在服务器端?您可以使用额外的 jvm 参数 -Djavax.net.debug=all 同时运行服务器和客户端并共享控制台输出吗?

标签: java post ssl https


【解决方案1】:

最后我成功了。

由于服务器证书是自签名的,我必须将其放入信任库中。为了避免将其添加到常见的 JRE cacerts,我使用以下命令将其放入单独文件中的信任库中(感谢Common SSL issues in Java

keytool -import -v -trustcacerts 
        -file servercert.crt -keystore server.jks 
        -keypass mypwd -storepass mypwd

然后我使用获得的信任库和包含密钥的客户端证书来初始化密钥库并将它们指定到 SSL 上下文(感谢sql.ru: Received fatal alert: handshake_failure):

String pwd = "mypwd";
InputStream keyStoreUrl = new FileInputStream("client.p12");
InputStream trustStoreUrl = new FileInputStream("server.jks");

KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(keyStoreUrl, pwd.toCharArray());
KeyManagerFactory keyManagerFactory = 
    KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, pwd.toCharArray());

KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(trustStoreUrl, pwd.toCharArray());
TrustManagerFactory trustManagerFactory = 
    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);

final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(keyManagerFactory.getKeyManagers(), 
                trustManagerFactory.getTrustManagers(), 
                new SecureRandom());
SSLContext.setDefault(sslContext);

我还必须指定HostnameVerifier,因为服务器证书和此服务器的 url 存在一些不一致:

HostnameVerifier allHostsValid = new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        //...
    }
};

HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

就是这样。更简单的是:

url = new URL(targetURL);

connection = (HttpsURLConnection) url.openConnection();            
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", 
                              "application/x-www-form-urlencoded;charset=utf-8");
connection.setRequestProperty("Accept", "application/x-www-form-urlencoded");
connection.setRequestProperty("Accept-Charset", "UTF-8");
connection.setRequestProperty("Content-Length", 
                              Integer.toString(Data.getBytes("utf8").length));
connection.setDoInput(true);
connection.setDoOutput(true);

connection.getOutputStream().write(Data.getBytes("utf8"));
// read response...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-14
    • 2012-01-10
    • 2013-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多