来自CA的PKCS12 Java密钥库和java中的用户证书

我最近负责用Java模拟Apple产品(iPhone配置实用程序)。 我一直有点困惑的部分之一是关于Exchange ActiveSync的一部分。 在那里,它允许您从钥匙串中选择一个证书作为您的EAS帐户的凭据。 经过一些研究,我发现它实际上是在创建一个PKCS12密钥库,插入我选择的证书的私钥,并将其编码为XML。 到目前为止还没什么大不了的。 如果我使用Keychain Access创建一个.p12文件,它上传没有问题。 但是当我尝试将其转移到Java时,我遇到了一个问题。

假设我将之前使用过的.s12文件中的一个作为.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密钥库。 使用客户端身份validation时,密钥库还需要包含私钥,在这种情况下,您使用KeyStore.getInstance(“PKCS12”)。

当您不使用客户端身份validation但仅使用服务器身份validation(并且私钥不会添加到密钥库,因为它属于服务器)时,最好使用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(); }