2013-12-17 103 views
1

我有(由第三方組織,我需要與溝通) 如何通過HttpsURLConnection爲POST請求使用X509證書?

  • 我的客戶證書

    1. 自簽名的服務器證書,包含密鑰,該服務器證書籤名。

    現在我需要通過使用這些證書的HTTPS發送POST請求。 我設法測試在Internet Explorer中通過https連接後我在瀏覽器上安裝他們:

    1. 服務器證書 - 到可信CA
    2. 客戶端證書 - 到個人證書。

    在java中到現在爲止我使用的代碼,在回答中SO: Java client certificates over HTTPS/SSL鑑於neu242,即接受的任何證書。但是現在服務器端確實接受了這一點,即我得到了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[]{}; 
        } 
    } 
    

    我想我應該指定客戶端證書以某種方式,但無法找到任何方式。 我當然可能是錯的。

    希望有人能帶領我正確的方向。

  • +0

    在客戶端或服務器端握手失敗?你可以使用額外的jvm參數'-Djavax.net.debug = all'運行你的服務器和客戶端,並共享控制檯輸出嗎? – thiyaga

    回答

    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,也出現了一些矛盾與服務器證書,該服務器的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...