我需要有关如何正确构建 Java Private.pem 的帮助。我有一个用 java 创建的 ECDSA pem 文件和一个用 python 创建的文件。我已经正确实现了 python 的实现,但 java 的实现不正确。
KeyPair pair = GenerateKeys();
PrivateKey priv = pair.getPrivate();
PublicKey pub = pair.getPublic();
// Convert to Bytes then Hex for new account params
byte[] bytePriv = priv.getEncoded();
byte[] bytePub = pub.getEncoded();
// save keys
SaveKeyToFile(bytePriv, "private", "private");
// Calls this function
public static void SaveKeyToFile(byte[] Key, String filename, String keyType) throws IOException, NoSuchAlgorithmException, NoSuchAlgorithmException, InvalidKeySpecException {
StringWriter stringWriter = new StringWriter();
PemWriter pemWriter = new PemWriter(stringWriter);
PemObjectGenerator pemObject = new PemObject("EC " + keyType.toUpperCase() + " KEY", Key);
pemWriter.flush();
pemWriter.close();
String privateKeyString = stringWriter.toString();
FileUtils.writeStringToFile(new File(filename + ".pem"), privateKeyString);
}
对于 Java 和 Python 的 OpenSSL 的 ASN1 解析(由于其长度不包括 hexdump):
JAVA
0:d=0 hl=3 l= 141 cons: SEQUENCE
3:d=1 hl=2 l= 1 prim: INTEGER :00
6:d=1 hl=2 l= 16 cons: SEQUENCE
8:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
17:d=2 hl=2 l= 5 prim: OBJECT :secp256k1
24:d=1 hl=2 l= 118 prim: OCTET STRING [HEXDUMP]
PYTHON
0:d=0 hl=2 l= 116 cons: SEQUENCE
2:d=1 hl=2 l= 1 prim: INTEGER :01
5:d=1 hl=2 l= 32 prim: OCTET STRING [HEXDUMP]
39:d=1 hl=2 l= 7 cons: cont [ 0 ]
41:d=2 hl=2 l= 5 prim: OBJECT :secp256k1
48:d=1 hl=2 l= 68 cons: cont [ 1 ]
50:d=2 hl=2 l= 66 prim: BIT STRING
对于输出PEM:
Java:
-----BEGIN EC PRIVATE KEY-----
MIGNAgEAMBAGByqGSM49AgEGBSuBBAAKBHYwdAIBAQQgiEc2TOzXj4mrUisuv+0p
xZ/z+Z/VyIvWug17zjNOPIKgBwYFK4EEAAqhRANCAATWi28vsiZdfqbqodDZc1uz
UFwNcu90l2ezayH0L4ZYB+MfReMCBFG/P6kn12GLj3DipqmvRucgmOlFVmggm+nH
-----END EC PRIVATE KEY-----
Python:
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEINTaEQvUvtOQlanp0lWv0KBcSs8IltKYH26OkoNxLQc5oAcGBSuBBAAK
oUQDQgAEcp3sseSpAXzIzWCwswYnsVnIZ0EEtkXl/CJWChQHilWLwWsxGq11/g/6
38UfZbsjE5TSf6zT8VXvTZE++u9c+A==
-----END EC PRIVATE KEY-----
非常感谢您的帮助!
最佳答案
(抱歉耽搁了。)
好吧,您确实需要特定于算法的形式,但 JCE PrivateKey.getEncoded()
返回 PKCS8(处理所有算法)编码,这与 How can I convert a .p12 to a .pem containing an unencrypted PKCS#1 private key block? 的 Q 基本相同。除非您想要 ECC 而不是 RSA,并且(更重要的是)您正在使用 Java 和 BouncyCaSTLe。
所以,您的选择是:
以 PEM 或直接二进制 (DER) 形式编写 PKCS8 编码,并使用
openssl ec
或在 1.1.0openssl pkey -traditional
中将其转换为算法特定的 PEM 形式,即 SEC1(rfc5915 基本上是 SEC1 的重新发布)。编写 PKCS8 编码(PEM 或 DER)并使用
openssl asn1parse -strparse
提取算法特定部分,或确定算法特定部分的偏移量并提取它直接地。 256 位 ECC key 对 DER 编码大部分为单八位字节长度,但标识(命名)曲线的 OID 对于不同的曲线可能大小不同;如您的示例所示,secp256k1 的大小为 24+2,因此您只需将 Arrays.copyOfRange(Key,26,Key.length) 赋予当前逻辑即可。对于大于 256 位的 ECC key 对,DER 编码可能需要变化,并且接近边界可能取决于 key 对中的公共(public)值是未压缩还是压缩形式,您没有实际的方法来控制( JCE 不提供编码内的选项)。这种情况下至少需要手动处理部分 DER,在这种情况下我会转而选择下一个选项。
BC(您已经在使用)公开了操作 PKCS8 结构的能力,您可以使用它来提取特定于算法的 SEC1 编码,如下所示:
import org.bouncycastle.asn1.ASN1Object; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; // as defined by PKCS8 ... PrivateKey k = /* whatever */; PemWriter w = new PemWriter (/* underlying writer */); PrivateKeyInfo i = PrivateKeyInfo.getInstance(ASN1Sequence.getInstance(k.getEncoded())); if( ! i.getPrivateKeyAlgorithm().getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey) ){ throw new Exception ("not EC key"); } ASN1Object o = (ASN1Object) i.parsePrivateKey(); w.writeObject (new PemObject ("EC PRIVATE KEY", o.getEncoded("DER"))); // DER may already be the default but safer to (re)specify it w.close();
关于java - Java中ECDSA的PEM编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47777264/