tl; dr:使用自定義CA而不將其添加到永久密鑰庫。在Java中運行時加載CA根證書
我正在編寫一個Java應用程序,該應用程序應該使用HTTPS連接到遠程服務器。該連接的代碼已準備就緒,但服務器的SSL證書由StartSSL簽署,該證書不在Java的CA根證書存儲區中。
使用this code,我從網站的有效證書信息,如https://www.google.com/
:
Response Code : 200
Cipher Suite : TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Cert Type : X.509
Cert Hash Code : -1643391404
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509
Cert Type : X.509
Cert Hash Code : 771393018
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509
Cert Type : X.509
Cert Hash Code : 349192256
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509
相同的代碼拋出一個SSLHandshakeException
我的域(姑且稱之爲https://www.example.com/
)。
當然,我可以使用keytool
(甚至可以使用Runtime.exec("keytool ...")
)手動將證書添加到密鑰存儲區,但這是骯髒的方式。我現在計劃的是將StartSSL根證書添加到我的應用程序文件中進行分發,然後在運行時將其加載到某個臨時密鑰庫中。通過這種方式,「真正」的密鑰庫保持不變。
從我已閱讀here和here,我將不得不亂用一些類,比如TrustManager
。我甚至找到了一種方法來completely turn off the validation,但這不是我想要的,因爲它似乎消除了加密通信的全部目的。
我甚至發現了一些代碼行,似乎做的正是我想要的,但我無法弄清楚如何調用哪些值作爲參數傳遞這種方法即:
public class SSLClasspathTrustStoreLoader {
public static void setTrustStore(String trustStore, String password) throws Exception {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream keystoreStream = SSLClasspathTrustStoreLoader.class.getResourceAsStream(trustStore);
keystore.load(keystoreStream, password.toCharArray());
trustManagerFactory.init(keystore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustManagers, null);
SSLContext.setDefault(sc);
}
}
有沒有人在以前類似的情況?我將不勝感激任何有關如何處理此問題的建議。如果你甚至可以幫助我獲得上面的代碼,我會非常高興!
關於你的答案的'keytool'部分:應用程序將被分發到多個客戶端系統。由於用戶可以更改密碼(默認情況下爲「changeit」,所以有人可能會這樣做),但手動修改系統密鑰庫對於每個單一系統都是不可能的,並且在運行時修改它是很困難的。在這種情況下,連接將不起作用,或者我需要提示輸入不是用戶友好的密碼。不過謝謝你的回答,我很快就會再來一次。 – Hexaholic
您可以創建自定義密鑰存儲運行時(如果其中一個不存在或者可以將密鑰存儲與您的應用程序一起打包)。我不知道你的項目的細節,但應該有更優雅的解決方案。無論如何,祝你好運:) – Boris