簡體   English   中英

以編程方式將證書添加到 Java KeyStore 的選項

[英]Options for Programmatically Adding Certificates to Java KeyStore

我收到 SSL 握手異常錯誤:PKIX“路徑未鏈接”( 此處描述)。 我通過使用 openssl 導入證書鏈來修復它:

openssl s_client -host www.envmgr.com -port 443 -showcerts > cert_chain.crt

並將其安裝到我的 JDK 的密鑰庫中:

keytool -import -alias envmgrchain -file cert_chain.crt -keystore cacerts -storepass changeit

嗯,這有效。 萬歲。 問題是我們將把我們的應用程序放在像 rackspace 或 AWS 這樣的雲服務器上,我認為我們很有可能無法修改 JVM 的密鑰庫來添加這個鏈。

我想,“沒問題,我只需以編程方式將此證書鏈添加到密鑰庫中”,所以我從我的 JVM 中刪除了它:

keytool -delete -alias envmgrchain -keystore cacerts -storepass changeit

並添加了以下代碼:

    KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    //Create an empty keystore that we can load certificate into
    trustStore.load(null);
    InputStream fis = new FileInputStream("cert_chain.crt");
    BufferedInputStream bis = new BufferedInputStream(fis);

    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    while(bis.available()>0) {
        Collection<? extends Certificate> certs = cf.generateCertificates(bis);
        Iterator<? extends Certificate> iter = certs.iterator();
        //Add each cert in the chain one at a time
        for(int i=0; i<certs.size(); i++) {
            Certificate cert = iter.next();
            String alias = "chaincert"+((i>0)?i:"");
            trustStore.setCertificateEntry(alias, cert);
        }
    }
    bis.close();
    fis.close();
//Add custom keystore to TrustManager
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(trustStore);
    SSLContext ctx = SSLContext.getInstance("TLSv1");
    ctx.init(null, tmf.getTrustManagers(), null);

但是當我運行它時,PKIX 錯誤再次出現。 上面的代碼不等價於 keytool -import 嗎? 我覺得我要么錯誤地將證書添加到 KeyStore,要么我沒有以正確的方式將 Keystore 安裝到 TrustManager 中。

僅供參考:我也試圖通過實施 X509TrustManager 來解決這個問題。

以下代碼可用於客戶端在運行時以編程方式添加 CA。 您無需將其放在任何商店中 - 只需隨身攜帶 PEM 編碼文件即可。 您甚至可以將其硬編碼到您的程序中,因此無需管理單獨的文件。

static String CA_FILE = "ca-cert.pem";
...

FileInputStream fis = new FileInputStream(CA_FILE);
X509Certificate ca = (X509Certificate) CertificateFactory.getInstance("X.509")
                        .generateCertificate(new BufferedInputStream(fis));

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry(Integer.toString(1), ca);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
...

您將需要一個受信任的分發渠道,以確保您的程序在等待被挑選的服務器上或在安裝時沿着線路傳輸時不會被修改。


openssl s_client -host www.envmgr.com -port 443 -showcerts > cert_chain.crt

您應該只需要信任根證書,而不是整個鏈。 服務器負責發送構建鏈所需的所有中間證書。 如果服務器沒有發送構建鏈所需的所有中間證書,則服務器配置錯誤。

您遇到的問題稱為“哪個目錄”問題。 它是 PKI 中的一個眾所周知的問題。 從本質上講,這意味着客戶端不知道去哪里獲取丟失的中間證書。 您可以通過讓服務器發送所有必需的中間件以及服務器的證書來解決它。 請參閱 OWASP 的 TLS 備忘單和規則 - 始終提供所有需要的證書


只是自行車脫落,但這里有一個完整的 Java 蠕蟲(尤其是 Java 7 及更低版本):

SSLContext ctx = SSLContext.getInstance("TLSv1");
ctx.init(null, tmf.getTrustManagers(), null);

如果需要,您可以改進它。 請參閱哪些密碼套件中的SSLSocketFactoryEx 以啟用 SSL 套接字? . 它彌補了 Java SSLSocketFactory默認提供的協議版本、密碼套件等方面的一些差距。

別。 您不應該修改 JRE 附帶的文件。 下一次升級,您的更新將消失。 您應該發布自己的信任庫,該信任庫由 JRE 附帶信任庫以及您想要信任的任何額外證書構建而成。 這是您的應用程序構建的一部分。

如果您想在運行時修改您自己的信任庫,請繼續,但是您需要注意 JVM 在重新啟動之前不一定會看到更改:它肯定不會在您使用的相同SSLContext中看到它們獲取要添加的證書。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM