如何通过HttpsURLConnection将X509证书用于POST请求?

时间:2013-12-17 06:23:20

标签: java post ssl https

我有

  1. 自签名服务器证书(来自我需要与之通信的第三方组织)
  2. 我的客户端证书,包含由此服务器证书签名的密钥。
  3. 现在我需要使用这些证书通过HTTPS发送POST请求。 我在浏览器中安装后,设法在Internet Explorer中通过https测试连接:

    1. 服务器证书 - 进入受信任的CA
    2. 客户证书 - 进入个人证书。
    3. 在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[]{};
          }
      }
      

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

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

1 个答案:

答案 0 :(得分:0)

最后我设法让它发挥作用。

由于服务器证书是自签名的,我不得不将其放入信任库。为避免将其添加到常见的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,因为与服务器证书和此服务器的网址存在一些不一致:

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...