我有一个客户端证书,由两个文件(.CRT和.Key)组成,我希望将其导入到Java密钥存储库中,然后在SSLVIEW中使用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: 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。
堆栈跟踪。注意,你不应该叫setCertificateEntry(),只叫setKeyEntry()。
您还需要用KeyManager而不是TrustManager初始化SSLContext。否则将永远找不到您的私钥。
您也不能将私钥强制转换为公钥。这些都没有什么意义。
@EJP对PublicKey的演员阵容感到抱歉,这是我在玩的东西,不小心抄到了问题上。你说得对,钥匙管理器的事。我修改了这个问题。
好吧,这个例外只能说明它所说的:密钥文件有问题。你能用openssl读吗?
@EJP只是试了一下,没有抱怨什么。
EJPS: OpenSSL可以根据类型(RSA 4)透明地为PrimeAtKEY读取几个PEM格式,但是Java,除非您添加BC,根本不能读取PEM,而只有一种DER格式(PKCS8未加密)。
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"的区别(背景)
非常感谢您的澄清,我选择使用BouncyCastle来读取私钥,并且似乎正在做它的工作。但是,我无法获得要处理的请求,我得到以下异常:"pkix路径生成失败:sun.security.provider.certpath.suncertpathbuilderexception:找不到请求目标的有效证书路径"。我读到这意味着证书不可信。我使用的代码与问题中的代码相同,只是我更改了几行代码,用BouncyCastle加载私钥。
这是一个不同的问题;您在keymanager中加载的用于验证您自己身份的privatekey+cert与trustmanager验证任何其他方(反之亦然)完全不同,与之毫无关系。但是您发布的代码似乎使用了一个没有初始化的tmf,这根本不起作用,并且抛出了一个与您所说的完全不同的异常。请检查您的代码,并(如果您仍然有问题)将您尝试连接时使用的信任库添加到您的Q中。
我以为我已经更新了问题中的代码,但显然没有。我正试图做到这一点:github.com/plailect/plaicdn/blob/master/plaicdn.py l123
现在,我尝试用openssl手动将证书和密钥导入到pkcs12密钥存储库,并按照下面的说明加载它:stackoverflow.com/questions/21223084/…但是在连接时一直收到相同的消息,我觉得此时应该打开一个新问题。
(1)好的,您的代码现在有效地使用(或者更确切地说,让JSSE使用)默认信任库(2)该python中引用的两个主机提供由"任天堂CA-G3"颁发的叶证书。CA当然不在Java默认CA列表中,尽管我不知道哪个商店(您的?)如果这个CA在任何标准的分布式CA列表中,而不进行任何修改,那么Python会使用我会非常惊讶的。您确定没有(按照一些说明或设置过程)将该证书添加到系统上的存储区吗?