Java 中 ECDSA 的 PEM 编码
PEM encoding of ECDSA in Java
我需要有关如何正确构建 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()
returns PKCS8(处理所有算法)编码,除了您之外,它与 几乎相同想要 ECC 而不是 RSA,并且(更重要的是)您正在使用 Java with BouncyCastle.
那么,您的选择是:
编写 PKCS8 编码,用 PEM 或直接用二进制 (DER),然后使用 openssl ec
或在 1.1.0 openssl pkey -traditional
中将其转换为算法-特定的 PEM 形式,即 SEC1(rfc5915 基本上是 SEC1 的 republication)。
编写PKCS8编码(PEM或DER均可),使用openssl asn1parse -strparse
提取算法特定部分,或者确定算法特定部分的偏移量,直接提取。一个 256 位 ECC 密钥对 DER 编码的长度大多为单八位字节,但标识(命名的)曲线的 OID 可能因不同的曲线而在大小上有所不同;如您的示例所示,secp256k1 为 24+2,因此您可以将 Arrays.copyOfRange(Key,26,Key.length)
赋给当前逻辑。
对于大于 256 位的 ECC 密钥对,DER 编码可能需要变化,并且接近边界线可能取决于密钥对中的 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 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()
returns PKCS8(处理所有算法)编码,除了您之外,它与
那么,您的选择是:
编写 PKCS8 编码,用 PEM 或直接用二进制 (DER),然后使用
openssl ec
或在 1.1.0openssl pkey -traditional
中将其转换为算法-特定的 PEM 形式,即 SEC1(rfc5915 基本上是 SEC1 的 republication)。编写PKCS8编码(PEM或DER均可),使用
openssl asn1parse -strparse
提取算法特定部分,或者确定算法特定部分的偏移量,直接提取。一个 256 位 ECC 密钥对 DER 编码的长度大多为单八位字节,但标识(命名的)曲线的 OID 可能因不同的曲线而在大小上有所不同;如您的示例所示,secp256k1 为 24+2,因此您可以将Arrays.copyOfRange(Key,26,Key.length)
赋给当前逻辑。对于大于 256 位的 ECC 密钥对,DER 编码可能需要变化,并且接近边界线可能取决于密钥对中的 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();