关于ssl:以编程方式将PEM证书导入Java KeyStore

Importing PEM certificate into Java KeyStore programmatically

我有一个客户端证书,由两个文件(.CRT和.Key)组成,我希望将其导入到Java密钥存储库中,然后在SSLVIEW中使用Apache的HTTPclient发送HTTP请求。然而,我似乎找不到一种程序化的方法来实现这一点,我发现的大多数其他问题要么指向外部工具,要么不适合我的情况。

我的证书用典型的"begin certificate"编码,后跟base64编码的字符串,密钥用"begin rsa private key"编码,然后是另一个base64编码的字符串。

这就是我到目前为止所得到的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static SSLContext createSSLContext(File certFile, File keyFile) throws IOException {
    try {
        PEMParser pemParser = new PEMParser(new FileReader(keyFile));
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(new BouncyCastleProvider());
        Object object = pemParser.readObject();
        KeyPair kp = converter.getKeyPair((PEMKeyPair) object);
        PrivateKey privateKey = kp.getPrivate();

        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        FileInputStream stream = new FileInputStream(certFile);
        X509Certificate cert = (X509Certificate) certFactory.generateCertificate(stream);

        KeyStore store = KeyStore.getInstance("JKS");
        store.load(null);
        store.setCertificateEntry("certificate", cert);
        store.setKeyEntry("private-key", privateKey,"changeit".toCharArray(), new Certificate[] { cert });

        SSLContext sslContext = SSLContexts.custom()
                .loadKeyMaterial(store,"changeit".toCharArray())
                .build();
        return sslContext;
    } catch (IOException | NoSuchAlgorithmException | CertificateException | KeyStoreException | KeyManagementException | UnrecoverableKeyException e) {
        throw new IOException(e);
    }
}

Stacktrace:

java.io.IOException: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at me.failedshack.ssltest.SSLTest.createSSLContext(SSLTest.java:80)
at me.failedshack.ssltest.SSLTest.main(SSLTest.java:31)

Caused by: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at java.base/sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:216)
at java.base/java.security.KeyFactory.generatePrivate(KeyFactory.java:390)
at me.failedshack.ssltest.SSLTest.createSSLContext(SSLTest.java:62)
... 1 more

Caused by: java.security.InvalidKeyException: invalid key format
at java.base/sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:330)
at java.base/sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:355)
at java.base/sun.security.rsa.RSAPrivateCrtKeyImpl.(RSAPrivateCrtKeyImpl.java:91)
at java.base/sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(RSAPrivateCrtKeyImpl.java:75)
at java.base/sun.security.rsa.RSAKeyFactory.generatePrivate(RSAKeyFactory.java:315)
at java.base/sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:212)
... 3 more

遗憾的是,在从文件生成私钥时,我一直得到一个InvalidKeyException。


RSA PRIVATE KEY类型的pem文件是base64而不是二进制文件,更重要的是pkcs1格式不是pkcs8,因此不能作为PKCS8EncodedKeySpec处理。

您的选择是:

  • 将PKCS1 PEM格式转换为PKCS8(未加密)PEM格式;读取并删除头和尾部行,并将Base64解码为二进制,并将其放在EDCOX1×1中,但您不需要外部工具,而且将私有密钥+证书(或链)转换为已经是Java密钥存储和AV的PKCS12(DER)也同样容易。回避这个问题

  • 将pkcs1 pem格式转换为pkcs8(未加密)der格式,您只需将其作为二进制文件读取并放入PKCS8EncodedKeySpec--同上

  • 如果pkcs1 pem是未加密的,则按照上述方法将其读取并解码为pkcs1 der,然后手动构造pkcs8(未加密)编码,并使用它

  • 如果pkcs1 pem是加密的,您可以检测到它的主体除了base64之外还包含两个822样式的头行,那么您必须复制openssl的"遗留"密钥文件解密,再构造pkcs8(未加密)编码。

  • 如果您可以使用bouncycastle,特别是bcpkix,它可以直接读取和解析openssl用于私钥的所有PEM变量,包括解密加密的PEM变量;但是,如果您还没有使用它,这是安装和/或部署的额外jar

请参阅以下一个或多个副本:向KeyStore(Java)加载证书(Q使用PojCyCARS构建PKCS8)Java:将DKIM私钥从RSA转换为DER,用于JavaMail(我的答案用手构造PKCS8)如何从文件加载RSA私钥(使用BouncyCastle读取)读取Java中的pkCS1格式的RSA私钥(使用PosiCyCar读取)从rsa.pem文件获取私钥(使用bc解密)用Java解密OpenSSL PEM编码的RSA私钥?(手动解密)可能是RSA私钥的pkcs_1和pkcs_8格式(背景)"begin rsa private key"和"begin private key"的区别(背景)