Rom*_*kiy 6 java ssl keystore pem sslsocketfactory
我得到了一个自签名的客户端证书工具包,该工具包将用于通过HTTPS访问服务器。该套件包含以下PEM文件:
解决任务的一种方法是生成Java密钥库:
...然后使用类似以下的代码来构建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实例。
问题是:是否有一种方法可以避免创建密钥库文件,并以某种方式从PublicKey和Certificate实例开始构建SSLSocketFactory(例如,可以使用bouncycastle的PemReader从PEM文件中获取)?
事实证明,仍必须构建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以编程方式获取KeyStore
在面临类似的挑战时,我之前对您的答案发表了评论,现在我回来提供加载 pem 文件的替代方案。我已经用它创建了一个库,以方便我自己和其他人使用,请参阅此处:GitHub - SSLContext Kickstart我希望你喜欢它:)
添加以下依赖项:
<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-pem</artifactId>
    <version>8.0.0</version>
</dependency>
pem 文件可以使用以下代码片段加载:
<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-pem</artifactId>
    <version>8.0.0</version>
</dependency>
回到你的主要问题,我还发现如果没有 KeyStores 就不可能创建 SSLSocketFactory。内存中的 KeyStore 可以按照您针对此用例的建议完美运行。
| 归档时间: | 
 | 
| 查看次数: | 5241 次 | 
| 最近记录: |