如何将ECDSA密钥转换为PEM格式

时间:2018-01-04 18:17:01

标签: openssl ecdsa

我有 myetherwallet 的私有原始密钥,带有密码短语" testwallet",现在我正在尝试使用OpenSSL将其转换为PEM格式按照这个答案。

echo "a140bd507a57360e2fa503298c035854f0dcb248bedabbe7a14db3920aaacf57" | xxd -r -p - | openssl ec -inform der -pubin -noout -passin pass:testwallet -text

但是出现了这个错误:

read EC key
unable to load Key
140084694296480:error:0D06B08E:asn1 encoding routines:ASN1_D2I_READ_BIO:not enough data:a_d2i_fp.c:247:

更新 我没有公钥,相反我想生成它以便以后我也可以生成以太网地址对应..

2 个答案:

答案 0 :(得分:9)

您声称您的原始密钥是OpenSSL的DER格式,但它并不是。此外,你要求私钥是一个公钥,它不是,并声称它的密码加密,这是错误的:公钥永远不会加密和OpenSSL中的私钥'传统'又名'遗产'特定于算法的DER格式(对于ECC,由SECG SEC1定义)无法加密。 (PKCS8格式的OTOH私钥可以在DER或PEM中进行密码加密,虽然PEM更方便。而且FWIW PKCS12格式始终是密码加密的,并且始终是DER。)

ECC(ECDSA,ECDH,ECMQV等)键始终相对于某些曲线' (更确切地说,曲线上的素数子群,其中已识别的生成器也称为基点)。对于比特币,这是 secp256k1 ,但你的问题并不是说它仅限于比特币,这个答案需要修改其他使用其他曲线的应用程序。

如果您还拥有公钥(作为未压缩点),您只需使用https://bitcoin.stackexchange.com/questions/66594/signing-transaction-with-ssl-private-key-to-pem中的解决方案即可。连接十六进制字符串:

  a pre_string : 30740201010420
  the privkey  : (32 bytes as 64 hexits) 
  a mid_string : a00706052b8104000aa144034200 (identifies secp256k1) 
  the pubkey   : (65 bytes as 130 hexits)

然后将十六进制转换为二进制并读取为DER,或将十六进制(可能通过二进制)转换为base64并用-----BEGIN/END EC PRIVATE KEY-----行换行以使其成为PEM。

如果您没有公钥,可以稍微修改一下。连接十六进制字符串

302e0201010420 privkey_32bytes_64hexits a00706052b8104000a 

并转换为二进制,然后读入openssl ec -inform d。注意OpenSSL将从给定曲线的私钥派生公钥,但实际上并不将其存储在PEM输出中,因此无法保证使用OpenSSL以外的软件进行读取。您可能需要使用openssl ec -text [-noout](在方便的PEM或DER输入上)来获取公钥值,然后返回并创建包含上述公钥的更全面的编码。

ADDED:,因为您似乎无法理解答案中的字词,我会尽可能详细地说明这一点。

a140bd507a57360e2fa503298c035854f0dcb248bedabbe7a14db3920aaacf57是以十六进制表示的原始私钥。 secp256k1私有值是二进制32字节;当二进制以十六进制表示时,每个字节采用两个十六进制数字,因此32字节需要64个十六进制数所有这些值都是原始私钥。没有任何由25位或25字节组成的部分具有任何有用的含义。不要拿这个价值的任何25个部分。

要构建没有公钥的私钥的OpenSSL / SECG表示,请将表示私钥的十六进制字符串 - 所有字符串,不加修改 - 放在另外两个十六进制字符串之间我展示了第二个选项:

 302e0201010420 a140bd507a57360e2fa503298c035854f0dcb248bedabbe7a14db3920aaacf57 a00706052b8104000a 

然后将这个组合的十六进制字符串转换为二进制,并将结果读入openssl ec -inform d

$ echo 302e0201010420 a140bd507a57360e2fa503298c035854f0dcb248bedabbe7a14db3920aaacf57 a00706052b8104000a | xxd -r -p >48101258.1
$ openssl ec -inform d <48101258.1
read EC key
writing EC key
-----BEGIN EC PRIVATE KEY-----
MC4CAQEEIKFAvVB6VzYOL6UDKYwDWFTw3LJIvtq756FNs5IKqs9XoAcGBSuBBAAK
-----END EC PRIVATE KEY-----

结果是PEM格式 - 但PEM格式不包括您指定所需的公钥。要查看包含派生公钥的字段,请添加-text;要仅查看字段而不是PEM输出,请添加-noout

$ openssl ec -inform d <48101258.1 -text -noout
read EC key
Private-Key: (256 bit)
priv:
    a1:40:bd:50:7a:57:36:0e:2f:a5:03:29:8c:03:58:
    54:f0:dc:b2:48:be:da:bb:e7:a1:4d:b3:92:0a:aa:
    cf:57
pub:
    04:20:ea:6d:8c:e7:bc:bb:48:33:69:b2:91:1c:75:
    e5:60:2a:34:28:be:44:96:e9:7f:14:ad:52:fd:4a:
    6a:a0:e3:60:83:9c:6e:db:32:2a:22:55:7c:70:1e:
    d0:fa:1e:06:cf:57:4f:be:17:bd:6a:85:51:69:c5:
    65:96:72:cf:a9
ASN1 OID: secp256k1

现在,如果您想要一个包含公钥的PEM格式密钥 ,请将两个私钥的十六进制字符串(全部64位)和新显示的公钥的十六进制值,并将其插入我的第一个选项。另请注意,ECC公钥是一个曲线点,可以是两种形式,压缩或未压缩;这里生成的表单是未压缩的。如果您需要压缩,我稍后会添加。未压缩格式的secp256k1点为65字节,以十六进制表示为130十六进制数字。 (其中openssl ec格式为4行,每行15个字节,剩下5个字节。)

$ echo 30740201010420 a140bd507a57360e2fa503298c035854f0dcb248bedabbe7a14db3920aaacf57 a00706052b8104000aa144034200 \
> 04:20:ea:6d:8c:e7:bc:bb:48:33:69:b2:91:1c:75: e5:60:2a:34:28:be:44:96:e9:7f:14:ad:52:fd:4a: \
> 6a:a0:e3:60:83:9c:6e:db:32:2a:22:55:7c:70:1e: d0:fa:1e:06:cf:57:4f:be:17:bd:6a:85:51:69:c5: \
> 65:96:72:cf:a9 | xxd -r -p >48101258.2
$ # note xxd -r -p ignores the colons; other hex programs may need them removed instead
$ openssl ec -inform d <48101258.2
read EC key
writing EC key
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIKFAvVB6VzYOL6UDKYwDWFTw3LJIvtq756FNs5IKqs9XoAcGBSuBBAAK
oUQDQgAEIOptjOe8u0gzabKRHHXlYCo0KL5Elul/FK1S/UpqoONgg5xu2zIqIlV8
cB7Q+h4Gz1dPvhe9aoVRacVllnLPqQ==
-----END EC PRIVATE KEY-----

为DavidS增加了2019-02:正如k06a's answer

中所示
  • 我的midstring的第一部分(或我的private-only选项的整个后缀)a00706052b8104000a是OID标记的上下文标记和长度a007,长度为{{1包含0605 1.3.132.0.10 which is secp256k1

  • 2b8104000a
  • 我的midstring a144034200的其余部分是一个上下文标记和长度,包含BITSTRING的标记长度和unused-bits标头,它是作为未压缩点的原始公钥。

要改为执行 secp256r1 又名P-256或prime256v1,您需要将AlgId.OID更改为编码为a00a 0608 2a8648ce3d030107的{​​{3}}。 p256r1的privatekey和publickey值与p256k1的大小相同,但是AlgId更长,因此您还需要更改外部SEQUENCE的长度

30770201010420 privatekey32bytes # note 77 
a00a06082a8648ce3d030107 a144034200 publicpoint65bytes 

答案 1 :(得分:1)

Elliptic Curve Private Key Format

ECPrivateKey ::= SEQUENCE {
 version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
 privateKey     OCTET STRING,
 parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
 publicKey  [1] BIT STRING OPTIONAL
}

所以publicKeyOPTIONAL,理论上可以错过。

以下是我的DER secp256k1私钥的示例:

30740201 01042092 E768CB72 0DC16924 27D156DB 39630748 0D1507B9 A4958450
2574B9A0 922F4BA0 0706052B 8104000A A1440342 00041954 9737B704 D1789A57
82E3430E 8259F904 71326081 054854D2 A5D096F9 686D05B0 30D98BA3 C60C056E
204CEF61 C0AC5B53 A9A6B9A0 5AFF9DA2 6CA4B65B 2E84

试图分解:

$ openssl asn1parse -inform DER -in <(echo "30740201 01042092 E768CB72 0DC16924 27D156DB 39630748 0D1507B9 A4958450 2574B9A0 922F4BA0 0706052B 8104000A A1440342 00041954 9737B704 D1789A57 82E3430E 8259F904 71326081 054854D2 A5D096F9 686D05B0 30D98BA3 C60C056E 204CEF61 C0AC5B53 A9A6B9A0 5AFF9DA2 6CA4B65B 2E84" | xxd -r -p)

ASN.1解析结果:

 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      [HEX DUMP]:92E768CB720DC1692427D156DB396307480D1507B9A49584502574B9A0922F4B
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  

详细(见https://bitcoin.stackexchange.com/a/66622/22979):

30 - ASN.1
74 - Length of all following bytes (116 bytes)

  02 - Type (integer)
  01 - Length of integer (1 byte)
  01 - Value of integer (1)

  04 - Type (octet string)
  20 - Length of string (32 bytes)
  92E768CB720DC1692427D156DB396307480D1507B9A49584502574B9A0922F4B - Private Key

  A0 - Tag 0
  07 - Length of tag (7 bytes)
  06 - Type (Object ID)
  05 - Length of the Object ID (5 bytes)
  2b 81 04 00 0a - The object ID of the curve secp256k1

  A1 - Tag 1
  44 - Length of tag (68 bytes)
  03 - Type – Bit string
  42 - Length of the bit string (66 bytes)
  00 - ???
  04 - Uncompressed Public Key
  19549737B704D1789A5782E3430E8259F90471326081054854D2A5D096F9686D - Public Key X coord
  05B030D98BA3C60C056E204CEF61C0AC5B53A9A6B9A05AFF9DA26CA4B65B2E84 - Public Key Y coord

我删除了Public Key对象并将ASN.1长度从116字节(0x74)固定为46字节(0x2e):

$ openssl asn1parse -inform DER -in <(echo "302E020101042092E768CB720DC1692427D156DB396307480D1507B9A49584502574B9A0922F4BA00706052B8104000A" | xxd -r -p)

得到了结果:

 0:d=0  hl=2 l=  46 cons: SEQUENCE          
 2:d=1  hl=2 l=   1 prim: INTEGER           :01
 5:d=1  hl=2 l=  32 prim: OCTET STRING      [HEX DUMP]:92E768CB720DC1692427D156DB396307480D1507B9A49584502574B9A0922F4B
39:d=1  hl=2 l=   7 cons: cont [ 0 ]        
41:d=2  hl=2 l=   5 prim: OBJECT            :secp256k1

尝试获取公钥:

$ openssl ec -inform DER -in <(echo "302E020101042092E768CB720DC1692427D156DB396307480D1507B9A49584502574B9A0922F4BA00706052B8104000A" | xxd -r -p)

结果:

read EC key
writing EC key
-----BEGIN EC PRIVATE KEY-----
MC4CAQEEIJLnaMtyDcFpJCfRVts5YwdIDRUHuaSVhFAldLmgki9LoAcGBSuBBAAK
-----END EC PRIVATE KEY-----

再试一次:

$ openssl ec -inform DER -text -in <(echo "302E020101042092E768CB720DC1692427D156DB396307480D1507B9A49584502574B9A0922F4BA00706052B8104000A" | xxd -r -p)

结果:

read EC key
Segmentation fault: 11

我使用了OSX系统openssl - 看起来像是LibreSSL 2.2.7

已添加:向LibreSSL报告错误:https://github.com/libressl-portable/portable/issues/395

然后我安装了最新的openssl:brew install openssl

/usr/local/Cellar/openssl/1.0.2n/bin/openssl ec -inform DER -text -noout -in <(echo "302E020101042092E768CB720DC1692427D156DB396307480D1507B9A49584502574B9A0922F4BA00706052B8104000A" | xxd -r -p)

得到了:

read EC key
Private-Key: (256 bit)
priv:
    00:92:e7:68:cb:72:0d:c1:69:24:27:d1:56:db:39:
    63:07:48:0d:15:07:b9:a4:95:84:50:25:74:b9:a0:
    92:2f:4b
pub: 
    04:19:54:97:37:b7:04:d1:78:9a:57:82:e3:43:0e:
    82:59:f9:04:71:32:60:81:05:48:54:d2:a5:d0:96:
    f9:68:6d:05:b0:30:d9:8b:a3:c6:0c:05:6e:20:4c:
    ef:61:c0:ac:5b:53:a9:a6:b9:a0:5a:ff:9d:a2:6c:
    a4:b6:5b:2e:84
ASN1 OID: secp256k1

最终解决方案:

$ /usr/local/Cellar/openssl/1.0.2n/bin/openssl ec -inform DER -text -noout -in <(cat <(echo -n "302e0201010420") <(echo -n "***") <(echo -n "a00706052b8104000a") | xxd -r -p) 2>/dev/null | tail -6 | head -5 | sed 's/[ :]//g' | tr -d '\n' && echo

用十六进制私钥替换***