java - Java中ECDSA的PEM编码

标签 java cryptography bouncycastle pem ecdsa

我需要有关如何正确构建 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.0 openssl 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/

相关文章:

java - 与变量相关的单词 'type' 是什么意思?

java - 解决多模块 Maven 项目中的临时依赖关系

c# - 'System.Security.Cryptography.CryptographicException' : keyset does not exist 类型异常

c - 如何在不使用 C 语言中的整数类型的情况下将 2 uint8 模乘一个大数

python - M2Crypto,立即加密和签名?

java - 除了 BouncyCaSTLe,还有其他开源 JCE 库吗?

java - 获取签名异常 : Could not verify signature

java - 如何在android中获取GSM值的EC/IO和GsmBitErrorRate?

java - 使用 Jsch 发出 Java 命令时遇到问题

c# - 使用 bouncycaSTLe 在 C# 中使用 cryptodome 解密在 python 中加密的 RSA 数据给出错误 block 不正确