无法将 .jks 转换为 .pkcs12:私钥过多
Unable to convert .jks to .pkcs12: excess private key
2017 年 12 月 30 日更新 – 4:
我已经设法解压缩 .jks
文件并从中提取密钥和证书。为此,我写了一个受 signerbox2
an open-source project that uses .jks
to sign data. In particular, I use jksreader
npm 包启发的小 nodejs 程序,它现在只存在了几天!
我写的程序是这样的:
const fs = require('fs');
jksreader = require('jksreader'),
pathToFile = process.argv[2],
password = process.argv[3],
contents = fs.readFileSync(pathToFile),
parsedContent = jksreader.parse(contents);
var key = jksreader.decode(parsedContent.material[0].key, password);
fs.writeFileSync('key', key);
for (var i = 0; i < parsedContent.material[0].certs.length; i++) {
var cert = parsedContent.material[0].certs[i];
fs.writeFileSync('cert' + i, cert);
}
这个程序是这样调用的:
node index.js /path/tp/my_key.jks my_password
输出看起来像一堆文件:
cert0
cert1
cert2
cert3
key
证书是DER格式的,可以这样读(注意-engine dstu
参数):
openssl x509 -in cert2 -inform der -text -noout -engine dstu
但是,我不知道如何读取(或转换为 PEM)密钥。我还在努力。 openssl asn1parse
在密钥文件上运行良好。这是 openssl asn1parse
output。我不确定从这里到哪里去。
2017 年 12 月 28 日更新 – 3:
我安装了 Keystore Explorer。它也无法提取私钥。它可以显示比我使用 keytool
所能获得的更多的信息。这里唯一奇怪的是,有两个 almost 同一个证书的相同副本,以我的名字命名(大写):
这是条目遵循 DSTU-4145 标准的指示:
2017 年 12 月 28 日更新 – 2:
.jks
中包含的密钥遵循或以某种方式与“DSTU-4145 签名方案”(算法)相关。这是乌克兰的政府标准。
我对签名方案几乎一无所知;例如,DSTU-4145 可以在 BouncyCastle specification page 上播种。
也许我需要安装 DSTU-4145 算法,以便 keytool
知道如何提取私钥?
2017 年 12 月 28 日更新 – 1:
在 Ubuntu 上的 java 以下版本下执行此操作:
$ java -version
java version "1.7.0_151"
OpenJDK Runtime Environment (IcedTea 2.6.11) (7u151-2.6.11-2ubuntu0.14.04.1)
OpenJDK 64-Bit Server VM (build 24.151-b01, mixed mode)
也不适用于 macOS 下的 Java 以下版本:
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
官方机构向我提供了 .jks
。
我想将 .jks
文件转换为 .pkcs12
以将其与 openssl 一起使用以实际签署某些内容。相反,我得到一个错误。
这就是我正在做的事情:
$ keytool -importkeystore \
-srckeystore my_keystore.jks \
-destkeystore my_keystore.pkcs12 \
-deststoretype pkcs12
Enter destination keystore password:
Re-enter new password:
Enter source keystore password:
Enter key password for <my_key>
keytool error: java.security.UnrecoverableKeyException: excess private key
不过,同时可以列出密钥:
$ keytool -list -keystore my_key.jks
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
my_key, Jan 18, 1970, PrivateKeyEntry,
Certificate fingerprint (SHA1): A1:B2:C3:D4:E5:F6:85:E4:2B:03:B9:68:FD:AE:9D:5B:24:CF:BF:FF
我做错了什么?
找到那个 JS 的工作做得很好。 (仅供参考,Stack 对每个评论只做一个@——幸运的是我在最近修改的内容中看到了这一点。)
现在我们可以看到您的私钥在 PKCS8 中具有 属性(合法但很少使用的功能),在重新检查时我发现标准 JCE 不支持(有一个在 decode
中评论但不在 parseKey
中评论)。看起来 BouncyCastle 可以——但 bcprov 不可以 JKS。
我建议您在此处继续使用 OpenSSL,因为这就是您最终想要的结果。
您绝对可以将证书文件从 binary/DER 转换为 PEM,方法是:
openssl x509 -in cert$i -inform der -out cert$i.pem -engine dstu
# or maybe pem$i or whatever names you find convenient
我很确定您可以使用以下方法将密钥文件转换为 PEM:
openssl pkey -in key -inform der -out key.pem -engine dstu
# or whatever name
# output defaults to unencrypted but your input is already unencrypted so no loss
但如果这不起作用,您可以手动完成:
(echo "-----BEGIN PRIVATE KEY-----"; openssl base64 <key; echo "-----END PRIVATE KEY-----") >key.pem
然后您应该能够将 PEM 文件提供给 openssl pkcs12 -export -engine dstu
以创建 PKCS12。
或者,由于 Whosebug 应该是关于编程的,我认为如果您使用 BC 提供程序读取密钥并编写 PKCS12,它会起作用,例如:
KeyFactory kf = KeyFactory.getInstance ("DSTU4145", "BC");
PrivateKey key = kf.generatePrivate (new PKCS8EncodedKeySpec (Files.readAllBytes("key")));
CertificateFactory cf = CertificateFactory.getInstance ("X.509", "BC");
// not sure we need the BC implementation for the certs, but can't hurt
Certificate[] certs = new Certificate[4];
for( int i = 0; i < 4; i++ ){
certs[i] = cf.generateCertificate (new FileInputStream ("cert"+i));
// or "cert"+i+".pem" -- CertificateFactory can read either DER or PEM
}
// or concatenate the cert PEM files into one and do
Certificate[] certs = cf.generateCertificates (new FileInputStream ("certs.pem")).toArray(new Certificate[0]);
// better to close the stream(s) when done like below but for input isn't vital
KeyStore ks = KeyStore.getInstance ("PKCS12", "BC"); ks.load(null);
char[] pw = "password".toCharArray(); // or preferably better value
ks.setKeyEntry("mykey", pkey, pw, certs);
try(OutputStream os = new FileOutputStream("result")){ ks.store(os, pw); }
2017 年 12 月 30 日更新 – 4:
我已经设法解压缩 .jks
文件并从中提取密钥和证书。为此,我写了一个受 signerbox2
an open-source project that uses .jks
to sign data. In particular, I use jksreader
npm 包启发的小 nodejs 程序,它现在只存在了几天!
我写的程序是这样的:
const fs = require('fs');
jksreader = require('jksreader'),
pathToFile = process.argv[2],
password = process.argv[3],
contents = fs.readFileSync(pathToFile),
parsedContent = jksreader.parse(contents);
var key = jksreader.decode(parsedContent.material[0].key, password);
fs.writeFileSync('key', key);
for (var i = 0; i < parsedContent.material[0].certs.length; i++) {
var cert = parsedContent.material[0].certs[i];
fs.writeFileSync('cert' + i, cert);
}
这个程序是这样调用的:
node index.js /path/tp/my_key.jks my_password
输出看起来像一堆文件:
cert0
cert1
cert2
cert3
key
证书是DER格式的,可以这样读(注意-engine dstu
参数):
openssl x509 -in cert2 -inform der -text -noout -engine dstu
但是,我不知道如何读取(或转换为 PEM)密钥。我还在努力。 openssl asn1parse
在密钥文件上运行良好。这是 openssl asn1parse
output。我不确定从这里到哪里去。
2017 年 12 月 28 日更新 – 3:
我安装了 Keystore Explorer。它也无法提取私钥。它可以显示比我使用 keytool
所能获得的更多的信息。这里唯一奇怪的是,有两个 almost 同一个证书的相同副本,以我的名字命名(大写):
这是条目遵循 DSTU-4145 标准的指示:
2017 年 12 月 28 日更新 – 2:
.jks
中包含的密钥遵循或以某种方式与“DSTU-4145 签名方案”(算法)相关。这是乌克兰的政府标准。
我对签名方案几乎一无所知;例如,DSTU-4145 可以在 BouncyCastle specification page 上播种。
也许我需要安装 DSTU-4145 算法,以便 keytool
知道如何提取私钥?
2017 年 12 月 28 日更新 – 1:
在 Ubuntu 上的 java 以下版本下执行此操作:
$ java -version
java version "1.7.0_151"
OpenJDK Runtime Environment (IcedTea 2.6.11) (7u151-2.6.11-2ubuntu0.14.04.1)
OpenJDK 64-Bit Server VM (build 24.151-b01, mixed mode)
也不适用于 macOS 下的 Java 以下版本:
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
官方机构向我提供了 .jks
。
我想将 .jks
文件转换为 .pkcs12
以将其与 openssl 一起使用以实际签署某些内容。相反,我得到一个错误。
这就是我正在做的事情:
$ keytool -importkeystore \
-srckeystore my_keystore.jks \
-destkeystore my_keystore.pkcs12 \
-deststoretype pkcs12
Enter destination keystore password:
Re-enter new password:
Enter source keystore password:
Enter key password for <my_key>
keytool error: java.security.UnrecoverableKeyException: excess private key
不过,同时可以列出密钥:
$ keytool -list -keystore my_key.jks
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
my_key, Jan 18, 1970, PrivateKeyEntry,
Certificate fingerprint (SHA1): A1:B2:C3:D4:E5:F6:85:E4:2B:03:B9:68:FD:AE:9D:5B:24:CF:BF:FF
我做错了什么?
找到那个 JS 的工作做得很好。 (仅供参考,Stack 对每个评论只做一个@——幸运的是我在最近修改的内容中看到了这一点。)
现在我们可以看到您的私钥在 PKCS8 中具有 属性(合法但很少使用的功能),在重新检查时我发现标准 JCE 不支持(有一个在 decode
中评论但不在 parseKey
中评论)。看起来 BouncyCastle 可以——但 bcprov 不可以 JKS。
我建议您在此处继续使用 OpenSSL,因为这就是您最终想要的结果。 您绝对可以将证书文件从 binary/DER 转换为 PEM,方法是:
openssl x509 -in cert$i -inform der -out cert$i.pem -engine dstu
# or maybe pem$i or whatever names you find convenient
我很确定您可以使用以下方法将密钥文件转换为 PEM:
openssl pkey -in key -inform der -out key.pem -engine dstu
# or whatever name
# output defaults to unencrypted but your input is already unencrypted so no loss
但如果这不起作用,您可以手动完成:
(echo "-----BEGIN PRIVATE KEY-----"; openssl base64 <key; echo "-----END PRIVATE KEY-----") >key.pem
然后您应该能够将 PEM 文件提供给 openssl pkcs12 -export -engine dstu
以创建 PKCS12。
或者,由于 Whosebug 应该是关于编程的,我认为如果您使用 BC 提供程序读取密钥并编写 PKCS12,它会起作用,例如:
KeyFactory kf = KeyFactory.getInstance ("DSTU4145", "BC");
PrivateKey key = kf.generatePrivate (new PKCS8EncodedKeySpec (Files.readAllBytes("key")));
CertificateFactory cf = CertificateFactory.getInstance ("X.509", "BC");
// not sure we need the BC implementation for the certs, but can't hurt
Certificate[] certs = new Certificate[4];
for( int i = 0; i < 4; i++ ){
certs[i] = cf.generateCertificate (new FileInputStream ("cert"+i));
// or "cert"+i+".pem" -- CertificateFactory can read either DER or PEM
}
// or concatenate the cert PEM files into one and do
Certificate[] certs = cf.generateCertificates (new FileInputStream ("certs.pem")).toArray(new Certificate[0]);
// better to close the stream(s) when done like below but for input isn't vital
KeyStore ks = KeyStore.getInstance ("PKCS12", "BC"); ks.load(null);
char[] pw = "password".toCharArray(); // or preferably better value
ks.setKeyEntry("mykey", pkey, pw, certs);
try(OutputStream os = new FileOutputStream("result")){ ks.store(os, pw); }