以编程方式将PEM证书导入Java KeyStore

时间:2018-07-15 22:47:09

标签: java ssl https apache-httpclient-4.x client-certificates

我有一个由两个文件(.crt和.key)组成的客户端证书,我希望将其导入到Java KeyStore中,然后在SSLContext中使用该证书与Apache的HTTPClient发送HTTP请求。但是,我似乎找不到以编程方式执行此操作的方法,我发现的大多数其他问题要么指向外部工具,要么不适合我的情况。

我的证书使用典型的'BEGIN CERTIFICATE'进行编码,后接Base64编码的字符串,然后使用'BEGIN RSA PRIVATE KEY'进行编码,然后使用另一个Base64编码的字符串进行编码。

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

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:无效的密钥格式       在我身上.failedshack.ssltest.SSLTest.createSSLContext(SSLTest.java:80)       在我身上.failedshack.ssltest.SSLTest.main(SSLTest.java:31)

     

原因:java.security.spec.InvalidKeySpecException:java.security.InvalidKeyException:无效的密钥格式       在java.base / sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:216)       在java.base / java.security.KeyFactory.generatePrivate(KeyFactory.java:390)       在我身上.failedshack.ssltest.SSLTest.createSSLContext(SSLTest.java:62)       ...还有1个

     

原因:java.security.InvalidKeyException:无效的密钥格式       在java.base / sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:330)       在java.base / sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:355)       在java.base / sun.security.rsa.RSAPrivateCrtKeyImpl。(RSAPrivateCrtKeyImpl.java:91)       在java.base / sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(RSAPrivateCrtKeyImpl.java:75)       在java.base / sun.security.rsa.RSAKeyFactory.generatePrivate(RSAKeyFactory.java:315)       在java.base / sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:212)       ...还有3个

可悲的是,从文件生成私钥时,我一直收到InvalidKeyException。

1 个答案:

答案 0 :(得分:1)

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

您的选择是:

  • 将PKCS1 PEM格式转换为PKCS8(未加密)PEM格式;读取并删除标题行和结尾行,并将base64解码为二进制并将其放入PKCS8EncodedKeySpec中-但是您说您不需要外部工具,而且转换私钥PLUS证书也很容易(或链)插入已经是Java密钥库的PKCS12(DER),并避免出现该问题

  • 将PKCS1 PEM格式转换为PKCS8(未加密)DER格式,您可以将其读取为二进制格式并放入PKCS8EncodedKeySpec-同上

  • 如果未加密PKCS1 PEM,则按上述方法将其读取并解码为PKCS1 DER,然后手动构建PKCS8(未加密)编码,并使用

  • 如果PKCS1 PEM是加密的,那么您可以检测到它,因为它的主体除了base64之外还包含两个822样式的标头行,您必须复制OpenSSL的“旧版”密钥文件解密,再构造PKCS8(未加密)编码

  • 如果可以专门使用bcpkix的BouncyCastle,它可以直接读取和解析OpenSSL用于私钥的所有 PEM变体,包括解密加密的私钥;但是,如果您尚未使用它,那将是一个额外的jar,用于安装和/或部署

查看以下一个或多个骗子:
Load certificate to KeyStore (JAVA)(Q使用BouncyCastle构造PKCS8)
Java: Convert DKIM private key from RSA to DER for JavaMail(我的答案“用手”构造了PKCS8)
How to Load RSA Private Key From File(使用BouncyCastle读取)
Read RSA private key of format PKCS1 in JAVA(使用BouncyCastle读取)
Get a PrivateKey from a RSA .pem file(使用BC解密)
Decrypting an OpenSSL PEM Encoded RSA private key with Java?(手动解密)
也许PKCS#1 and PKCS#8 format for RSA private key(背景)
Differences between "BEGIN RSA PRIVATE KEY" and "BEGIN PRIVATE KEY"(背景)