2010-09-01 163 views
12

我最近已經負責用Java模擬Apple產品(iPhone配置實用程序)。我一直堅持的一個部分是關於Exchange ActiveSync的一部分。在那裏,它允許您從鑰匙串中選擇一個證書作爲EAS帳戶的憑證。經過一番研究,我發現它實際上是在創建一個PKCS12密鑰庫,插入我選擇的證書的私鑰並將其編碼爲XML。到目前爲止沒有什麼大不了的。如果我使用Keychain Access創建了一個.p12文件,它可以毫無問題地上傳。但是當我嘗試將它帶入Java時,我遇到了一個問題。來自CA的PKCS12 Java Keystore和來自java的用戶證書

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

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密鑰庫?我是否在正確的軌道上?

在此先感謝!

回答

16

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

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

你在第二次嘗試時想要做的事(ks.setKeyEntry("SomeAlias", userCert.getPublicKey().getEncoded(), chain))是用公鑰替代應該是私鑰的公鑰(參見KeyStore#setKeyEntry)。

.cer文件往往只是證書而不是私鑰(儘管當然,擴展最終只是一個跡象)。如果您從密鑰鏈接Access.app導出您的.cer文件,您將不會得到私鑰(這是導出格式的.p12)。

編輯約KeychainStore:

如果你試圖做這種轉換的原因,歸根結底是訪問私鑰和那些已經在鑰匙串證書,你可以從KeychainStore直接加載它們:

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

針對此兩點要注意:

  • 任何非空,非空密碼的Wi我會盡量使用私鑰(例如"-".toCharArray()),因爲訪問將由操作系統的安全服務提示(就像在其他應用程序中一樣)。
  • 據我所知,還有一個錯誤,it only allows access to one private key/certificate pair(即使多對私鑰/證書對的出現在鑰匙串)
+0

+1,非常徹底! – 2010-09-01 11:53:31

+0

現貨。謝謝(你的)信息! – Staros 2010-09-01 14:13:58

3

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

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

當你不在使用客戶端身份驗證,但只有服務器的身份驗證(和私鑰不會被添加到密鑰存儲,因爲它屬於服務器),它能夠更好地使用 KeyStore.getInstance(「JKS」),比你將多個帶有別名的證書添加到該密鑰庫。

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

-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(); 
} 
+1

這個答案在這裏甚至沒有關係。問題主要是爲什麼他不能在PKCS12'KeyStore'中創建一個可信任的條目。您甚至不會演示PKCS#12信任存儲庫,也無法解釋爲何無法使用。 – erickson 2016-09-14 18:49:27