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

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

问题描述

我有一个由两个文件(.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.

解决方案

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

您的选择是:

  • 将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(未加密)编码

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

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

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.

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.

This is what I got so far:

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

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

解决方案

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.

Your choices are:

  • 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

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

  • 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

  • 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

  • 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

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天全站免登陆