将 CA 签名的 JKS 密钥库转换为 PEM

Posted

技术标签:

【中文标题】将 CA 签名的 JKS 密钥库转换为 PEM【英文标题】:Convert CA-signed JKS keystore to PEM 【发布时间】:2011-11-23 15:27:02 【问题描述】:

我有一个带有 CA 签名证书的 JKS 密钥库。我需要以 PEM 格式导出它才能与 nginx 一起使用。我需要以包含整个链的方式执行此操作,以便我的客户可以验证签名。

如果我这样做:

keytool -exportcert -keystore mykestore.jks -file mycert.crt -alias myalias
openssl x509 -out mycert.crt.pem -outform pem -in mycert.crt -inform der

它只包括最低级别的证书。验证失败:

$ openssl s_client -connect localhost:443
CONNECTED(00000003)
depth=0 /O=*.mydomain.com/OU=Domain Control Validated/CN=*.mydomain.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 /O=*.mydomain.com/OU=Domain Control Validated/CN=*.mydomain.com
verify error:num=27:certificate not trusted
verify return:1
depth=0 /O=*.mydomain.com/OU=Domain Control Validated/CN=*.mydomain.com
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/O=*.mydomain.com/OU=Domain Control Validated/CN=*.mydomain.com
   i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=123123
... (only one certificate!)
...
SSL-Session:
    ...
    Verify return code: 21 (unable to verify the first certificate)

来自 Java:

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

而具有相同 JKS 密钥库的 Jetty 会打印以下内容:

$ openssl s_client -connect localhost:8084
CONNECTED(00000003)
depth=2 /C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
verify error:num=19:self signed certificate in certificate chain
verify return:0
---
Certificate chain
 0 s:/O=*.mydomain.com/OU=Domain Control Validated/CN=*.mydomain.com
   i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=1234
 1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=1234
   i:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
 2 s:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
   i:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
...
SSL-Session:
    Verify return code: 19 (self signed certificate in certificate chain)

虽然 openssl 返回 19 错误,但它不再是 Java HttpsURLConnection 的问题,这就是我所关心的。

那么,我怎样才能从 JKS 中导出 整个链 格式(例如 PEM),同时适用于 nginx 服务器和 Java 客户端?我错过了什么?

【问题讨论】:

你问的最后一个问题的答案在我下面回答的末尾。 【参考方案1】:

您可以轻松地将 JKS 文件转换为 PKCS12 文件:

keytool -importkeystore -srckeystore keystore.jks -srcstoretype JKS -deststoretype PKCS12 -destkeystore keystore.p12

然后您可以提取私钥和任何证书:

openssl pkcs12 -in keystore.p12

【讨论】:

这是一个很好的答案 - 它怎么没有更多的支持? 同意,我真的不敢相信这有多好。【参考方案2】:

我经常遇到的一个比较大的问题是,在生成 CSR 以获取我们的证书时,密钥库(Sun 格式的 jks 密钥库)不输出 .key 或提供任何获取 .key 的工具。所以我总是以 .pem/.crt 结尾,无法将它与 Apache2 一起使用,它无法像 Tomcat 那样读取 JKS 密钥库,而是需要一个未打包的 .key + .pem/.crt 对。

首先,获取现有密钥库的“副本”并跳到下面的第 5 个命令,或者像这样创建自己的:

C:\Temp>keytool -genkey -alias tomcat -keyalg RSA -keystore
 keystore.jks -keysize 2048 -validity 730 -storepass changeit

然后,可选择创建一个为期 2 年的 CSR,然后在接下来的 3 步流程中导入 CSR 响应:

C:\Temp>keytool -certreq -alias mydomain -keystore keystore.jks
 -file mydomain.csr
C:\Temp>keytool -import -trustcacerts -alias root -file
 RootPack.crt -keystore keystore.jks -storepass changeit
C:\Temp>keytool -import -trustcacerts -alias tomcat -file mydomain.response.crt
 -keystore keystore.jks -storepass changeit

要使其正常工作,并且如果您已经拥有用于 Tomcat 应用程序服务器的 JKS 密钥库文件,请执行以下步骤:

首先,将 DER(二进制)格式的证书放入一个名为“exported-der.crt”的文件中:

C:\Temp>keytool -export -alias tomcat -keystore keystore.jks -file
 exported-der.crt

然后,查看并验证它:

C:\Temp>openssl x509 -noout -text -in exported-der.crt -inform der

现在您需要将其转换为 PEM 格式,该格式在 Apache 等应用程序中得到更广泛的应用,并由 OpenSSL 进行 PKCS12 转换:

C:\Temp>openssl x509 -in exported-der.crt -out exported-pem.crt 
-outform pem -inform der

然后,下载并使用 ExportPriv 从您的密钥库中获取未加密的私钥:

C:\Temp>java ExportPriv <keystore> <alias> <password> > exported-pkcs8.key

现在您可能已经意识到,私钥正在以 PKCS#8 PEM 格式导出。要将其转换为适用于 Apache (PKCS#12??) 的 RSA 格式,您可以发出以下命令:

C:\Temp>openssl pkcs8 -inform PEM -nocrypt -in exported-pkcs8.key
 -out exported-pem.key

【讨论】:

【参考方案3】:

我不确定是否可以使用keytool 提取链,但可以使用小型 Java 程序完成:

public void extract(KeyStore ks, String alias, char[] password, File dstdir) throws Exception

    KeyStore.PasswordProtection pwd = new KeyStore.PasswordProtection(password);
    KeyStore.PrivateKeyEntry entry = (KeyStore.PasswordKeyEntry)ks.getEntry(alias, pwd);
    Certificate[] chain = entry.getCertificateChain();
    for (int i = 0; i < chain.length; i++) 
        Certificate c = chain[i];
        FileOutputStream out = new FileOutputStream(new File(dstdir, String.format("%s.%d.crt", alias, i)));
        out.write(c.getEncoded());
        out.close();
    

此代码应将链的所有证书以DER格式写入提交目录中。

【讨论】:

以上是关于将 CA 签名的 JKS 密钥库转换为 PEM的主要内容,如果未能解决你的问题,请参考以下文章

将 Java 密钥库转换为 PEM 格式

将Java密钥库转换为PEM格式

如何将信任证书从 .jks 转换为 .pem?

需要帮助将 P12 证书转换为 JKS

从jks证书中提取公钥和私钥(jks证书转pem证书)

Android 根据源码文件生成.jks的系统签名