簡體   English   中英

來自CA的PKCS12 Java密鑰庫和java中的用戶證書

[英]PKCS12 Java Keystore from CA and User certificate in java

我最近負責用Java模擬Apple產品(iPhone配置實用程序)。 我有點堅持的其中一個部分是關於Exchange ActiveSync的一部分。 在那里,它允許您從鑰匙串中選擇一個證書作為您的EAS帳戶的憑據。 經過一些研究,我發現它實際上創建了一個PKCS12密鑰庫,插入了我選擇的證書的私鑰,並將其編碼為XML。 到目前為止還沒什么大不了的。 如果我使用Keychain Access創建一個.p12文件,它上傳沒有問題。 但是當我嘗試將其轉移到Java時,我遇到了一個問題。

假設我將之前使用的.p12文件中的一個證書導出為.cer文件(這是我們期望在環境中獲得的)。 現在當我將它上傳到Java時,我得到一個Certificate對象如下...

KeyStore ks = java.security.KeyStore.getInstance("PKCS12");
ks.load(null, "somePassword".toCharArray());

CertificateFactory cf = CertificateFactory.getInstance("X.509", new BouncyCastleProvider());
java.security.cert.Certificate userCert  = cf.generateCertificate(new FileInputStream("/Users/me/Desktop/RecentlyExportedCert.cer"));

但是當我嘗試......

ks.setCertificateEntry("SomeAlias", userCert);

我得到例外......

java.security.KeyStoreException: TrustedCertEntry not supported

因此,從證書中我轉移到密鑰上。 但是有了這些證書(我也獲得了CA證書),我只能訪問公鑰,而不是私鑰。 如果我試圖像這樣添加公鑰...

java.security.cert.Certificate[] chain = {CACert};
ks.setKeyEntry("SomeAlias", userCert.getPublicKey().getEncoded(), chain);

我明白了......

java.security.KeyStoreException: Private key is not stored as PKCS#8 EncryptedPrivateKeyInfo: java.io.IOException: DerValue.getOctetString, not an Octet String: 3

所以現在我在這里。 有沒有人知道如何從.cer文件中獲取私鑰到Java中的PKCS12密鑰庫? 我是否走在正確的軌道上?

提前致謝!

PKCS#12格式用於存儲與證書鏈相關聯的私鑰,兩者都是必需的(盡管您可能不需要整個鏈)。 盡管PKCS12密鑰庫類型在將此格式映射到Java KeyStore做得很好,但並非所有內容都支持此原因。

您在第一次嘗試時嘗試做的是自己存儲證書,這是行不通的。

您在第二次嘗試時嘗試做的事情( ks.setKeyEntry("SomeAlias", userCert.getPublicKey().getEncoded(), chain) )是為了代替應該是私鑰的公鑰(見KeyStore#setKeyEntry )。

.cer文件往往只適用於證書而不是私鑰(當然,擴展最終只是一個指示)。 如果從Keychain Access.app導出.cer文件,則不會獲得私鑰(這是.p12導出格式的用途)。

關於KeychainStore的編輯

如果您嘗試進行此轉換的原因最終是訪問已存在於鑰匙串中的私鑰和證書,則可以直接從KeychainStore加載它們:

KeyStore ks = KeyStore.getInstance("KeychainStore", "Apple");
ks.load(null, "-".toCharArray());

有幾個注意事項:

  • 任何非空的非空密碼都將使用私鑰(例如"-".toCharArray() ),因為操作系統的安全服務會提示訪問(就像在其他應用程序中一樣)。
  • 據我所知,仍然存在一個錯誤, 它只允許訪問一個私鑰/證書對 (即使密鑰鏈中存在多對私鑰/證書對)

http://www.docjar.com/html/api/org/bouncycastle/jce/examples/PKCS12Example.java.html

這是如何將具有關聯私鑰的證書添加到PKCS12密鑰庫。 使用客戶端身份驗證時,密鑰庫還需要包含私鑰,在這種情況下,您使用KeyStore.getInstance(“PKCS12”)。

當您不使用客戶端身份驗證但僅使用服務器身份驗證(並且私鑰不會添加到密鑰庫,因為它屬於服務器)時,最好使用KeyStore.getInstance(“JKS”),而不是使用該密鑰庫的別名。

當您使用PKCS12時,據我所知,您只能添加與私鑰相關聯的1個證書(您必須添加整個證書鏈),並且要用於該證書。

我參加聚會已經晚了幾年,但這花了我幾個小時才能正常工作,所以我認為值得發布一個有效的解決方案 此解決方案使用1).p12 / PKCS12證書和2)不在默認TrustManager中的CA(並且您希望以編程方式添加它而不是添加到默認的TrustManager)。 3)沒有第三方加密庫,只需將HttpClient全部整合在一起。

我還在JavaDoc中添加了一些有用的keytoolopenssl命令來處理證書,因為這本身就是一門藝術。

// Stitch it all together with HttpClient
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(getSSLSocketFactory()).build();


private SSLConnectionSocketFactory getSSLSocketFactory() {
    try {
        SSLContext sslContext = SSLContext.getInstance("TLS");

        KeyManager[] keyManager = getKeyManager("pkcs12", "path/to/cert.p12"), "p12_password"));
        TrustManager[] trustManager = getTrustManager("jks", "path/to/CA.truststore", "trust_store_password"));
        sslContext.init(keyManager, trustManager, new SecureRandom());

        return new SSLConnectionSocketFactory(sslContext);
    } catch (Exception e) {
        throw new RuntimeException("Unable to setup keystore and truststore", e);
    }
}

/**
 * Some useful commands for looking at the client certificate and private key:
 * keytool -keystore certificate.p12 -list -storetype pkcs12 -v
 * openssl pkcs12 -info -in certificate.p12
 */
private KeyManager[] getKeyManager(String keyStoreType, String keyStoreFile, String keyStorePassword) throws Exception {
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    keyStore.load(this.getClass().getClassLoader().getResourceAsStream(keyStoreFile), keyStorePassword.toCharArray());
    kmf.init(keyStore, keyStorePassword.toCharArray());

    return kmf.getKeyManagers();
}

/**
 * Depending on what format (pem / cer / p12) you have received the CA in, you will need to use a combination of openssl and keytool
 * to convert it to JKS format in order to be loaded into the truststore using the method below. 
 *
 * You could of course use keytool to import this into the JREs TrustStore - my situation mandated I create it on the fly.
 *
 * Useful command to look at the CA certificate:
 * keytool -keystore root_ca.truststore -list -storetype jks -v
 *
 */
private TrustManager[] getTrustManager(String trustStoreType, String trustStoreFile, String trustStorePassword) throws Exception {
    KeyStore trustStore = KeyStore.getInstance(trustStoreType);
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustStore.load(this.getClass().getClassLoader().getResourceAsStream(trustStoreFile), trustStorePassword.toCharArray());
    tmf.init(trustStore);

    return tmf.getTrustManagers();
}

暫無
暫無

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

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