最近,用Fabric JAVA SDK做enroll动作的时候,在读取private Key的时候出现了一个错误:
org.bouncycastle.openssl.PEMException: problem parsing PRIVATE KEY: java.lang.IllegalArgumentException: wrong version for private key info
at org.bouncycastle.openssl.PEMParser$PrivateKeyParser.parseObject(Unknown Source)
at org.bouncycastle.openssl.PEMParser.readObject(Unknown Source)
at main.SampleUser.getPrivateKeyFromBytes(SampleUser.java:163)
at main.SampleUser$1.getKey(SampleUser.java:86)
at org.hyperledger.fabric.sdk.User.userContextCheck(User.java:94)
at org.hyperledger.fabric.sdk.HFClient.setUserContext(HFClient.java:257)
at main.carDemo.main(carDemo.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:282)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalArgumentException: wrong version for private key info
at org.bouncycastle.asn1.pkcs.PrivateKeyInfo.<init>(Unknown Source)
at org.bouncycastle.asn1.pkcs.PrivateKeyInfo.getInstance(Unknown Source)
... 13 more
查看Openssl 版本
使用openssl verison
或者openssl version -a
查看。
OpenSSL 1.0.1e-fips 11 Feb 2013
built on: Mon Feb 20 14:38:48 UTC 2017
platform: linux-x86_64
options: bn(64,64) md2(int) rc4(16x,int) des(idx,cisc,16,int) idea(int) blowfish(idx)
compiler: gcc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DKRB5_MIT -m64 -DL_ENDIAN -DTERMIO -Wall -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -Wa,--noexecstack -DPURIFY -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM
OPENSSLDIR: "/etc/pki/tls"
engines: rdrand dynamic
定位问题
用线上生产环境的key测了下,没出现问题。初步判断,本地生成key的方式不同导致的。
在Stack Overflow上找了找。发现了一个回答,总结下:
-
核心原因
这个private key并不是PKC S8的格式。 -
详细说明
OpenSSL针对每种密码学算法支持四种PEM编码格式的private key。其中只有一种是PKCS8非加密的,也就是被Java PKCS8EncodedKeySpec所支持的(我用Node js压根就没有这个问题,还是推荐用node sdk啊)。也就是说我们可能使用了一种java并不支持的格式生成了private key文件。
这里,给出了一个链接(后续整理): How can I convert a .p12 to a .pem containing an unencrypted PKCS#1 private key block?针对RSA算法的转化进行了说明。
但是对于EC算法,有两个“传统的”编码格式是根据SEC1定义的(与RSA的PKCS1相同)。同时,PKCS8编码格式(对于所有的算法,并不仅限于RSA和EC)是被定义为PKCS8的。OpenSSL可以处理多种格式是因为PEM文件有两个特性:- BEGIN开头(非常重要)
- END结尾(第二重要)
这两个信息定义了数据的类型。
EC PRIVATE KEY
代表了SEC1,PRIVATE KEY
代表了PKCS8。
这里,我们有两种方法:
- convert the OpenSSL key to PKCS8 unencrypted PEM with
openssl pkcs8 -topk8 -nocrypt
or justopenssl pkey
(in 1.0.0 up) -- or generate it as PKCS8 in the first place withgenpkey
or possiblyreq -newkey
(both in 1.0.0 up) instead ofecparam -genkey
. Use that blob de-base64-ed in PKCS8EncodedKeySpec。 - if in addition to bcprov you have or get bcpkix, that handles many (maybe all?) OpenSSL formats that plain Java does not. Rebuild, or just revert to, the original PEM format (which can be in memory) and you can parse with PEMReader and then convert with JcaPEMKeyConverter (or just a KeyFactory).
这里有一个RSA文件转化的实例。EC算法可以参考这个改一下。
自己实现PEMParser
当然,我们可以自己实现PEMParser的功能,前提是我们已经获得了PEM的数据。这里有个例子:
import java.security.*;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
...
static void SO48996981BCparseECprivate () throws Exception {
byte[] server_sec1 = DatatypeConverter.parseBase64Binary("MHgCAQEEIA27nM1klj9pVxOzJrO4aBLFvXTtOJnf+vMhiv3HA+3noAsGCSskAwMCCAEBB6FEA0IABG1erLtLyTbC5yN8gVY4a0JPO5eefKftWMbSQij2Ks5TaNNuj/tqigFqsk1g/l2UBBkIx2KdpeiY8nVddwMpzho=");
ASN1Sequence seq = ASN1Sequence.getInstance(server_sec1);
org.bouncycastle.asn1.sec.ECPrivateKey pKey = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(seq);
AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, pKey.getParameters());
byte[] server_pkcs8 = new PrivateKeyInfo(algId, pKey).getEncoded();
KeyFactory fact = KeyFactory.getInstance ("EC","BC");
PrivateKey pkey = fact.generatePrivate (new PKCS8EncodedKeySpec(server_pkcs8));
// for test only:
System.out.println (pkey.getClass().getName() + " " + pkey.getAlgorithm());
}
最后,其实我们并不需要java来做这件事情,OpenSSL完全提供了ECDH(其他算法也ok?)的agreement/derivation。所以,可以直接使用OpenSSL脚本来执行。
网友评论