无法将.jks转换为.pkcs12:超出私钥

3

2017年12月30日更新-4:

我已经成功解包了.jks文件并从中提取了密钥和证书。为了做到这一点,我编写了一个小的nodejs程序,受signerbox2开源项目启发,该项目使用.jks来签署数据。特别地,我使用了jksreadernpm包,它只存在几天!

我编写的程序看起来像这样:

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输出。我不确定下一步该怎么做。
更新于2017年12月28日-3:
我安装了 Keystore Explorer。它也无法提取私钥。它可以显示比我用 keytool 获得的更多信息。唯一奇怪的事情是,有两个几乎完全相同的证书副本,以我的名字(大写)命名:

enter image description here

这里的提示表示该条目遵循DSTU-4145标准:

enter image description here

2017年12月28日更新-2:

.jks文件中包含的密钥遵循或与“DSTU-4145签名方案”(算法)相关。这是乌克兰的一项政府标准。

我对签名方案几乎一无所知;例如,DSTU-4145可以在BouncyCastle 规范页面上找到。

也许我需要安装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

我做错了什么?

你能检查一下你的密钥库中是否有重复的密钥吗?我认为你已经导入了它,这会导致“过多的私钥”。 - Andrei Sfat
@sfat 您是指如何检查?您的意思是我可能意外创建了 my_key.pkcs12 吗?不,我刚刚检查过,任何地方都没有这样的文件。 - oldhomemovie
(1) keytool -list 带有 -v 参数应该显示完整的证书链,而重复的名称确实很奇怪,但无法帮助查找私钥问题。(2) 使用该密钥的代码需要像 BC 这样的提供程序,但 keytool 不需要。(3) 错误消息在 sun.security.pkcs.PKCS8Key.parseKey 中,并提示写入系统中存在错误,但我无法从 sun.security.provider.KeyProtector 中进行隔离,这使得确认此问题非常困难。该机构是否以其他形式提供密钥(和证书)?他们是否提供任何测试或虚拟密钥,你可以安全地公开使用? - dave_thompson_085
1
从我看到的情况来看,人们通常会遇到相同的问题,即密钥库具有重复条目或者密钥库和密钥密码使用的密码不匹配且应该相同。我不确定您的设置是否有不同的密码或者您使用了相同的密码。另外,您是否安装了JCE(http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html)?这些问题很难解决,因为有很多东西由于明显的原因而无法公开披露 :) - Andrei Sfat
@sfat @dave_thompson_085 感谢你们的精彩评论!我已经成功解压了 .jks 文件:我现在有了证书和密钥,它们都是单独的文件。现在我正在努力确定密钥的格式,以便能够实际使用它。 - oldhomemovie
1个回答

2
很棒,找到了那个JS。(顺便说一下,在评论中Stack只允许一个@,很幸运我在最近修改中看到了这一点。)
从中我们现在可以看到您的私钥具有PKCS8中的属性(这是一个合法但非常少用的功能),重新检查后发现标准JCE不支持它(在decode中有注释但在parseKey中没有)。似乎BouncyCastle做到了——但bcprov不支持JKS。
我建议您继续使用OpenSSL,因为这正是您想要的结果。您肯定可以将证书文件从二进制/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格式:

使用以下命令将密钥文件转换为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。

或者,由于StackOverflow是关于编程的,我认为如果您使用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); }

嗨,戴夫,这真是太棒了,谢谢!我会接受这个答案的。还有几个问题:你是如何确定密钥在PKCS8中的呢?我不知道如何运行Java程序+安装/包含依赖库(例如BouncyCastle)。我现在会开始弄清楚这个问题,但也许你可以帮我一下,并发布一个完整的程序清单? - oldhomemovie
我想我会从https://www.bouncycastle.org/fips-java/BCFipsIn100.pdf和这个示例程序清单开始。两者都是很好的来源。 - oldhomemovie
@gmile:我知道JKS格式使用PKCS8加密私钥,因为我以前曾经使用过它;我猜测被描述为“读取器”的东西会保持PKCS8格式,你提供的asn1parse输出证实了这一点。除非你有某种原因需要符合FIPS标准——而我认为使用乌克兰数据的人可能不是美国政府的一部分——否则我建议不要限制自己在Bouncy或其他任何东西的FIPS模式中,因为这很可能会强制额外的努力来解决其限制,而这并没有真正对你有益。... - dave_thompson_085
Bouncy cryptoprovider的工作方式与其他Java cryptoproviders相同--这是Java Crypto Architecture的一个旨在重要特性--因此,您可以只使用标准Java文档(也称为javadoc)来使用KeyFactory CertificateFactory等,_除非_您在某些调用中传递"BC"作为提供程序参数(如我所示),并且在运行时具有bcprov jar。如果您有关于Java编程的一般问题,那可能应该是一个不同的问题--并且可能已经得到了回答,因为SO有许多关于Java编程的Q&A。 - dave_thompson_085

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接