以编程方式将 PEM 证书导入 Java KeyStore [英] Importing PEM certificate into Java KeyStore programmatically

查看:66
本文介绍了以编程方式将 PEM 证书导入 Java KeyStore的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a client certificate composed of two files (.crt and .key) which I wish to import to a java KeyStore to then use in a SSLContext to sent HTTP requests with Apache's HTTPClient. However, I can't seem to find a way to do this programmatically, most other questions I've found either point to external tools or aren't fit for my case.

我的证书使用典型的BEGIN CERTIFICATE"编码,后跟 Base64 编码字符串,密钥使用BEGIN RSA PRIVATE KEY",然后是另一个 Base64 编码字符串.

My certificate is encoded with the typical 'BEGIN CERTIFICATE' followed by a Base64 encoded string, and the key with 'BEGIN RSA PRIVATE KEY' and then another Base64 encoded string.

这是我目前得到的:

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);
    }
}

堆栈跟踪:

java.io.IOException: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: 无效的密钥格式在 me.failedshack.ssltest.SSLTest.createSSLContext(SSLTest.java:80)在 me.failedshack.ssltest.SSLTest.main(SSLTest.java:31)

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)

引起: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)在 me.failedshack.ssltest.SSLTest.createSSLContext(SSLTest.java:62)... 1个

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在 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个

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.

Sadly I keep getting an InvalidKeyException when generating the private key from the file.

推荐答案

RSA PRIVATE KEY 类型的 PEM 文件是 base64 而非二进制,更重要的是采用 PKCS1 格式而非 PKCS8,因此无法处理作为 PKCS8EncodedKeySpec.

A PEM file of type RSA PRIVATE KEY is base64 not binary and more importantly is in PKCS1 format NOT PKCS8 and thus cannot be processed as a PKCS8EncodedKeySpec.

您的选择是:

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

  • convert the PKCS1 PEM format to PKCS8 (unencrypted) PEM format; read that and drop the header and trailer lines and decode the base64 to binary and put that in PKCS8EncodedKeySpec -- but you say you don't want external tools, plus it's just as easy to convert the privatekey PLUS cert (or chain) into a PKCS12 (DER) which is already a Java keystore and avoid the issue

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

convert the PKCS1 PEM format to PKCS8 (unencrypted) DER format, which you can just read as binary and put in PKCS8EncodedKeySpec -- ditto

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

if the PKCS1 PEM is unencrypted, read and decode it as above to PKCS1 DER then manually construct the PKCS8 (unencrypted) encoding, and use that

如果 PKCS1 PEM 已加密,您可以检测到,因为除了 base64 之外,它的主体还包含两个 822 样式的标题行,您必须复制 OpenSSL 的传统"密钥文件解密,加上构建 PKCS8(未加密)编码

if the PKCS1 PEM is encrypted, which you can detect because its body contains two 822-style header lines in addition to the base64, you have to replicate OpenSSL's 'legacy' key file decryption, PLUS construct the PKCS8 (unencrypted) encoding

如果你可以专门使用BouncyCastlebcpkix,它可以直接读取和解析所有OpenSSL 用于私钥的PEM 变体,包括解密加密的;但是,如果您尚未使用它,则需要额外安装和/或部署 jar

if you can use BouncyCastle specifically bcpkix, it can directly read and parse all the PEM variants used by OpenSSL for privatekeys, including decrypting the encrypted ones; however, if you're not already using it, that's an additional jar to install and/or deploy

查看这些骗局中的一个或多个:
将证书加载到 KeyStore (JAVA)(Q 使用 BouncyCastle 构建 PKCS8)
Java:将 DKIM 私钥从 RSA 转换为 DER用于 JavaMail(我的答案是手工"构造 PKCS8)
如何从文件加载 RSA 私钥(使用 BouncyCastle 读取)
在JAVA中读取PKCS1格式的RSA私钥(使用 BouncyCastle 读取)
从 RSA .pem 文件中获取私钥(解密使用BC)
使用 Java 解密 OpenSSL PEM 编码的 RSA 私钥?(手动解密)
也许 RSA 私钥的 PKCS#1 和 PKCS#8 格式(背景)
BEGIN RSA PRIVATE KEY"之间的区别和开始私钥"(背景)

See one or more of these dupes:
Load certificate to KeyStore (JAVA) (Q constructs PKCS8 using BouncyCastle)
Java: Convert DKIM private key from RSA to DER for JavaMail (my answer constructs PKCS8 'by hand')
How to Load RSA Private Key From File (reads using BouncyCastle)
Read RSA private key of format PKCS1 in JAVA (reads using BouncyCastle)
Get a PrivateKey from a RSA .pem file (decrypt using BC)
Decrypting an OpenSSL PEM Encoded RSA private key with Java? (decrypt manually)
maybe PKCS#1 and PKCS#8 format for RSA private key (background)
and Differences between "BEGIN RSA PRIVATE KEY" and "BEGIN PRIVATE KEY" (background)

这篇关于以编程方式将 PEM 证书导入 Java KeyStore的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆