如何从PEM证书和密钥构建SSLSocketFactory而不转换为密钥库?

我获得了一个自签名客户端证书工具包,用于通过HTTPS访问服务器。 该套件包含以下PEM文件:

  1. client.crt(客户端证书)
  2. client.key(客户端私钥)
  3. ca.crt(CA证书)

解决该任务的一种方法是生成Java密钥库:

  1. 使用openssl将客户端证书和密钥转换为PKCS12密钥库
  2. 使用keytool将CA证书导入到商店

…然后使用以下代码构建SSLSocketFactory实例:

InputStream stream = new ByteArrayInputStream(pksData); KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(stream, password); KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, password.toCharArray()); KeyManager[] keyManagers = kmf.getKeyManagers(); TrustManagerFactory tmfactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmfactory.init(keyStore); TrustManager[] trustManagers = tmfactory.getTrustManagers(); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(keyManagers, trustManagers, null); sslSocketFactory = sslContext.getSocketFactory(); 

…后来用于初始化http库。

因此,我们获得了一个KeyStore,然后在它的帮助下初始化KeyManagers和TrustManagers,最后我们用它们构建了SSLSocketFactory实例。

问题是 :有没有办法避免密钥库文件的创建,并以某种方式构建SSLSocketFactory,从PublicKey和Certificate实例开始(例如,可以使用bouncycastle的PemReader从PEM文件中获取)?

事实certificate,仍然需要构建KeyStore实例,但它可以在内存中完成(从PEM文件作为输入开始),而不使用使用keytool构建的中间密钥库文件。

要构建内存中的KeyStore,可以使用以下代码:

 private static final String TEMPORARY_KEY_PASSWORD = "changeit"; private KeyStore getKeyStore() throws ConfigurationException { try { Certificate clientCertificate = loadCertificate(certificatePem); PrivateKey privateKey = loadPrivateKey(privateKeyPem); Certificate caCertificate = loadCertificate(caPem); KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(null, null); keyStore.setCertificateEntry("ca-cert", caCertificate); keyStore.setCertificateEntry("client-cert", clientCertificate); keyStore.setKeyEntry("client-key", privateKey, TEMPORARY_KEY_PASSWORD.toCharArray(), new Certificate[]{clientCertificate}); return keyStore; } catch (GeneralSecurityException | IOException e) { throw new ConfigurationException("Cannot build keystore", e); } } private Certificate loadCertificate(String certificatePem) throws IOException, GeneralSecurityException { CertificateFactory certificateFactory = CertificateFactory.getInstance("X509"); final byte[] content = readPemContent(certificatePem); return certificateFactory.generateCertificate(new ByteArrayInputStream(content)); } private PrivateKey loadPrivateKey(String privateKeyPem) throws IOException, GeneralSecurityException { return pemLoadPrivateKeyPkcs1OrPkcs8Encoded(privateKeyPem); } private byte[] readPemContent(String pem) throws IOException { final byte[] content; try (PemReader pemReader = new PemReader(new StringReader(pem))) { final PemObject pemObject = pemReader.readPemObject(); content = pemObject.getContent(); } return content; } private static PrivateKey pemLoadPrivateKeyPkcs1OrPkcs8Encoded( String privateKeyPem) throws GeneralSecurityException, IOException { // PKCS#8 format final String PEM_PRIVATE_START = "-----BEGIN PRIVATE KEY-----"; final String PEM_PRIVATE_END = "-----END PRIVATE KEY-----"; // PKCS#1 format final String PEM_RSA_PRIVATE_START = "-----BEGIN RSA PRIVATE KEY-----"; final String PEM_RSA_PRIVATE_END = "-----END RSA PRIVATE KEY-----"; if (privateKeyPem.contains(PEM_PRIVATE_START)) { // PKCS#8 format privateKeyPem = privateKeyPem.replace(PEM_PRIVATE_START, "").replace(PEM_PRIVATE_END, ""); privateKeyPem = privateKeyPem.replaceAll("\\s", ""); byte[] pkcs8EncodedKey = Base64.getDecoder().decode(privateKeyPem); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey)); } else if (privateKeyPem.contains(PEM_RSA_PRIVATE_START)) { // PKCS#1 format privateKeyPem = privateKeyPem.replace(PEM_RSA_PRIVATE_START, "").replace(PEM_RSA_PRIVATE_END, ""); privateKeyPem = privateKeyPem.replaceAll("\\s", ""); DerInputStream derReader = new DerInputStream(Base64.getDecoder().decode(privateKeyPem)); DerValue[] seq = derReader.getSequence(0); if (seq.length < 9) { throw new GeneralSecurityException("Could not parse a PKCS1 private key."); } // skip version seq[0]; BigInteger modulus = seq[1].getBigInteger(); BigInteger publicExp = seq[2].getBigInteger(); BigInteger privateExp = seq[3].getBigInteger(); BigInteger prime1 = seq[4].getBigInteger(); BigInteger prime2 = seq[5].getBigInteger(); BigInteger exp1 = seq[6].getBigInteger(); BigInteger exp2 = seq[7].getBigInteger(); BigInteger crtCoef = seq[8].getBigInteger(); RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(modulus, publicExp, privateExp, prime1, prime2, exp1, exp2, crtCoef); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePrivate(keySpec); } throw new GeneralSecurityException("Not supported format of a private key"); } 

这个想法来自PEM的Programmatically Obtain KeyStore