我有一个由两个文件(.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。
答案 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"(背景)