OpenSSL 生成的 pem 密钥是否与 Erlang Crypto 兼容
Are OpenSSL generated pem keys compatible with Erlang Crypto
我已经使用 OpenSSL 生成了私钥:
ARWIN-TIO:/tmp$ openssl ecparam -name prime256v1 -genkey -noout -out key.pem
ARWIN-TIO:/tmp$ cat key.pem
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIINcLTcsL/VhTBEsp1gRgHtO9lLzypm7oYjwViz3bWZCoAoGCCqGSM49
AwEHoUQDQgAE8kuZsfDhQdkkYjVRla3ShxAlsbLwOt8jUsKyebB7GGWxnBiDqRoB
bSxkkd+APIM/4+lYwIDAx5+EmIIuUIRdcA==
-----END EC PRIVATE KEY-----
我正在努力在 Erlang 的加密模块中使用它:
ARWIN-TIO:/tmp$ erl
Erlang/OTP 21 [erts-10.3.5.6] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
Eshell V10.3.5.6 (abort with ^G)
1> Message = <<"Hello">>.
<<"Hello">>
2> PrivateKey = base64:decode("MHcCAQEEIINcLTcsL/VhTBEsp1gRgHtO9lLzypm7oYjwViz3bWZCoAoGCCqGSM49AwEHoUQDQgAE8kuZsfDhQdkkYjVRla3ShxAlsbLwOt8jUsKyebB7GGWxnBiDqRoBbSxkkd+APIM/4+lYwIDAx5+EmIIuUIRdcA==").
<<48,119,2,1,1,4,32,131,92,45,55,44,47,245,97,76,17,44,
167,88,17,128,123,78,246,82,243,202,153,...>>
3> Signature = crypto:sign(ecdsa, sha256, Message, [PrivateKey, prime256v1]).
** exception error: badkey
in function crypto:sign/5
called as crypto:sign(ecdsa,sha256,<<"Hello">>,
[<<48,119,2,1,1,4,32,131,92,45,55,44,47,245,97,76,17,44,
167,88,17,128,123,78,...>>,
prime256v1],
[])
当我使用 Erlang 的加密模块生成密钥时,它起作用了:
ARWIN-TIO:/tmp$ erl
Erlang/OTP 21 [erts-10.3.5.6] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
Eshell V10.3.5.6 (abort with ^G)
1> Message = <<"Hello">>.
<<"Hello">>
2> {PublicKey, PrivateKey} = crypto:generate_key(ecdh, crypto:ec_curve(prime256v1)).
{<<4,149,38,43,104,132,214,232,147,174,88,185,96,250,185,
181,170,8,231,61,255,134,143,255,4,136,249,9,...>>,
<<130,195,50,229,108,51,72,27,219,145,250,244,116,3,52,
234,13,60,148,175,112,192,140,110,232,46,116,...>>}
3> Signature = crypto:sign(ecdsa, sha256, Message, [PrivateKey, prime256v1]).
<<48,70,2,33,0,252,243,117,254,110,176,232,185,121,156,93,
105,74,115,115,247,83,82,17,32,167,254,223,74,...>>
我注意到 Erlang 私钥比 OpenSSL 私钥短得多:
4> base64:encode(PrivateKey).
<<"gsMy5WwzSBvbkfr0dAM06g08lK9wwIxu6C50rwKaBvw=">>
比 "openssl ecparam -name prime256v1 -genkey -noout -out key.pem"
生成的短得多
为什么 OpenSSL 密钥 different/not 与 Erlang 的加密模块一起工作?我怎样才能使它们兼容?
谢谢。
您看到的是 X9.62 编码的私钥,另外还进行了 PEM 编码。您已经删除了 PEM header 行和 base 64 解码,所以现在您只剩下 X9.62 编码的私钥。
由于您似乎在使用 OpenSSL,因此首先解码 base64,这样您就可以:
openssl asn1parse -inform DER -in private_prime256v1.bin
哪个会给你:
0:d=0 hl=2 l= 119 cons: SEQUENCE
2:d=1 hl=2 l= 1 prim: INTEGER :01
5:d=1 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:
835C2D372C2FF5614C112CA75811807B4EF652F3CA99BBA188F0562CF76D6642
39:d=1 hl=2 l= 10 cons: cont [ 0 ]
41:d=2 hl=2 l= 8 prim: OBJECT :prime256v1
51:d=1 hl=2 l= 68 cons: cont [ 1 ]
53:d=2 hl=2 l= 66 prim: BIT STRING
您没有清楚地显示 public / 私钥的完整扩展,但看起来它们只是秘密 S 的 "flat" 表示,对于 public键,未压缩点W.
所以这将是解析私钥中的OCTET STRING(最后的BIT STRING包含可选的public密钥,未压缩点W):
835C2D372C2FF5614C112CA75811807B4EF652F3CA99BBA188F0562CF76D6642
所以这解释了为什么。我不完全知道如何解析这样的结构,但你可能不需要。似乎 Erlang 已经有一个名为 public_key:pem_decode
的函数可以为您执行此操作,它应该只获取整个 openssl
生成的密钥。根据 the documentation,它还应该解析私钥。
由于我不是 Erlang 程序员,我无法轻松测试,所以请告诉我。
在 Maarten 的指导下,我设法弄明白了(不知道我的 OpenSSL 私钥是 "X9.62" 编码的;我以为它只是在 base64 中)。原来需要使用public_key
模块,而不是crypto
模块。
这是一个如何使用 Erlang 的 OpenSSL 密钥的示例。
生成私钥:
ARWIN-TIO:/tmp$ openssl ecparam -name prime256v1 -genkey -noout -out private_key.pem
ARWIN-TIO:/tmp$ cat private_key.pem
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIGpSqNErkMHbjdeBQBI6NdlK8QgFluCJvGhkt3g5n5zboAoGCCqGSM49
AwEHoUQDQgAE43/FB66JPZ2JCbL+e7b4TVpDJAOeeaHmy7NPYL1cOVJFRBux91M4
QYBu1s5DigaQdi/Qz7KK/Yr5EMktgulXHA==
-----END EC PRIVATE KEY-----
和对应的public键:
ARWIN-TIO:/tmp$ openssl ec -in private_key.pem -pubout -out public_key.pub
ARWIN-TIO:/tmp$ cat public_key.pub
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE43/FB66JPZ2JCbL+e7b4TVpDJAOe
eaHmy7NPYL1cOVJFRBux91M4QYBu1s5DigaQdi/Qz7KK/Yr5EMktgulXHA==
-----END PUBLIC KEY-----
然后在 Erlang 中签署这样的消息:
(2020-01-02 20:51:56) ARWIN-TIO:/tmp$ erl
Erlang/OTP 21 [erts-10.3.5.6] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
Eshell V10.3.5.6 (abort with ^G)
1> Message = <<"My Secret Message">>,
1>
1> PrivatePem = lists:nth(1, public_key:pem_decode(<<"-----BEGIN EC PRIVATE KEY-----
1> MHcCAQEEIGpSqNErkMHbjdeBQBI6NdlK8QgFluCJvGhkt3g5n5zboAoGCCqGSM49
1> AwEHoUQDQgAE43/FB66JPZ2JCbL+e7b4TVpDJAOeeaHmy7NPYL1cOVJFRBux91M4
1> QYBu1s5DigaQdi/Qz7KK/Yr5EMktgulXHA==
1> -----END EC PRIVATE KEY-----">>)),
1>
1> PrivateKey = public_key:pem_entry_decode(PrivatePem),
1> Signature = public_key:sign(Message, sha256, PrivateKey).
<<48,68,2,32,64,80,146,169,96,232,174,140,196,59,46,54,
107,199,145,184,86,181,79,168,165,107,54,157,222,...>>
你可以这样验证:
2> PublicPem = lists:nth(1, public_key:pem_decode(<<"-----BEGIN PUBLIC KEY-----
2> MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE43/FB66JPZ2JCbL+e7b4TVpDJAOe
2> eaHmy7NPYL1cOVJFRBux91M4QYBu1s5DigaQdi/Qz7KK/Yr5EMktgulXHA==
2> -----END PUBLIC KEY-----">>)),
2>
2> PublicKey = public_key:pem_entry_decode(PublicPem),
2> VerifyResult = public_key:verify(Message, sha256, Signature, PublicKey),
2> VerifyResult.
true
我已经使用 OpenSSL 生成了私钥:
ARWIN-TIO:/tmp$ openssl ecparam -name prime256v1 -genkey -noout -out key.pem
ARWIN-TIO:/tmp$ cat key.pem
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIINcLTcsL/VhTBEsp1gRgHtO9lLzypm7oYjwViz3bWZCoAoGCCqGSM49
AwEHoUQDQgAE8kuZsfDhQdkkYjVRla3ShxAlsbLwOt8jUsKyebB7GGWxnBiDqRoB
bSxkkd+APIM/4+lYwIDAx5+EmIIuUIRdcA==
-----END EC PRIVATE KEY-----
我正在努力在 Erlang 的加密模块中使用它:
ARWIN-TIO:/tmp$ erl
Erlang/OTP 21 [erts-10.3.5.6] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
Eshell V10.3.5.6 (abort with ^G)
1> Message = <<"Hello">>.
<<"Hello">>
2> PrivateKey = base64:decode("MHcCAQEEIINcLTcsL/VhTBEsp1gRgHtO9lLzypm7oYjwViz3bWZCoAoGCCqGSM49AwEHoUQDQgAE8kuZsfDhQdkkYjVRla3ShxAlsbLwOt8jUsKyebB7GGWxnBiDqRoBbSxkkd+APIM/4+lYwIDAx5+EmIIuUIRdcA==").
<<48,119,2,1,1,4,32,131,92,45,55,44,47,245,97,76,17,44,
167,88,17,128,123,78,246,82,243,202,153,...>>
3> Signature = crypto:sign(ecdsa, sha256, Message, [PrivateKey, prime256v1]).
** exception error: badkey
in function crypto:sign/5
called as crypto:sign(ecdsa,sha256,<<"Hello">>,
[<<48,119,2,1,1,4,32,131,92,45,55,44,47,245,97,76,17,44,
167,88,17,128,123,78,...>>,
prime256v1],
[])
当我使用 Erlang 的加密模块生成密钥时,它起作用了:
ARWIN-TIO:/tmp$ erl
Erlang/OTP 21 [erts-10.3.5.6] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
Eshell V10.3.5.6 (abort with ^G)
1> Message = <<"Hello">>.
<<"Hello">>
2> {PublicKey, PrivateKey} = crypto:generate_key(ecdh, crypto:ec_curve(prime256v1)).
{<<4,149,38,43,104,132,214,232,147,174,88,185,96,250,185,
181,170,8,231,61,255,134,143,255,4,136,249,9,...>>,
<<130,195,50,229,108,51,72,27,219,145,250,244,116,3,52,
234,13,60,148,175,112,192,140,110,232,46,116,...>>}
3> Signature = crypto:sign(ecdsa, sha256, Message, [PrivateKey, prime256v1]).
<<48,70,2,33,0,252,243,117,254,110,176,232,185,121,156,93,
105,74,115,115,247,83,82,17,32,167,254,223,74,...>>
我注意到 Erlang 私钥比 OpenSSL 私钥短得多:
4> base64:encode(PrivateKey).
<<"gsMy5WwzSBvbkfr0dAM06g08lK9wwIxu6C50rwKaBvw=">>
比 "openssl ecparam -name prime256v1 -genkey -noout -out key.pem"
生成的短得多为什么 OpenSSL 密钥 different/not 与 Erlang 的加密模块一起工作?我怎样才能使它们兼容?
谢谢。
您看到的是 X9.62 编码的私钥,另外还进行了 PEM 编码。您已经删除了 PEM header 行和 base 64 解码,所以现在您只剩下 X9.62 编码的私钥。
由于您似乎在使用 OpenSSL,因此首先解码 base64,这样您就可以:
openssl asn1parse -inform DER -in private_prime256v1.bin
哪个会给你:
0:d=0 hl=2 l= 119 cons: SEQUENCE
2:d=1 hl=2 l= 1 prim: INTEGER :01
5:d=1 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:
835C2D372C2FF5614C112CA75811807B4EF652F3CA99BBA188F0562CF76D6642
39:d=1 hl=2 l= 10 cons: cont [ 0 ]
41:d=2 hl=2 l= 8 prim: OBJECT :prime256v1
51:d=1 hl=2 l= 68 cons: cont [ 1 ]
53:d=2 hl=2 l= 66 prim: BIT STRING
您没有清楚地显示 public / 私钥的完整扩展,但看起来它们只是秘密 S 的 "flat" 表示,对于 public键,未压缩点W.
所以这将是解析私钥中的OCTET STRING(最后的BIT STRING包含可选的public密钥,未压缩点W):
835C2D372C2FF5614C112CA75811807B4EF652F3CA99BBA188F0562CF76D6642
所以这解释了为什么。我不完全知道如何解析这样的结构,但你可能不需要。似乎 Erlang 已经有一个名为 public_key:pem_decode
的函数可以为您执行此操作,它应该只获取整个 openssl
生成的密钥。根据 the documentation,它还应该解析私钥。
由于我不是 Erlang 程序员,我无法轻松测试,所以请告诉我。
在 Maarten 的指导下,我设法弄明白了(不知道我的 OpenSSL 私钥是 "X9.62" 编码的;我以为它只是在 base64 中)。原来需要使用public_key
模块,而不是crypto
模块。
这是一个如何使用 Erlang 的 OpenSSL 密钥的示例。
生成私钥:
ARWIN-TIO:/tmp$ openssl ecparam -name prime256v1 -genkey -noout -out private_key.pem
ARWIN-TIO:/tmp$ cat private_key.pem
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIGpSqNErkMHbjdeBQBI6NdlK8QgFluCJvGhkt3g5n5zboAoGCCqGSM49
AwEHoUQDQgAE43/FB66JPZ2JCbL+e7b4TVpDJAOeeaHmy7NPYL1cOVJFRBux91M4
QYBu1s5DigaQdi/Qz7KK/Yr5EMktgulXHA==
-----END EC PRIVATE KEY-----
和对应的public键:
ARWIN-TIO:/tmp$ openssl ec -in private_key.pem -pubout -out public_key.pub
ARWIN-TIO:/tmp$ cat public_key.pub
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE43/FB66JPZ2JCbL+e7b4TVpDJAOe
eaHmy7NPYL1cOVJFRBux91M4QYBu1s5DigaQdi/Qz7KK/Yr5EMktgulXHA==
-----END PUBLIC KEY-----
然后在 Erlang 中签署这样的消息:
(2020-01-02 20:51:56) ARWIN-TIO:/tmp$ erl
Erlang/OTP 21 [erts-10.3.5.6] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
Eshell V10.3.5.6 (abort with ^G)
1> Message = <<"My Secret Message">>,
1>
1> PrivatePem = lists:nth(1, public_key:pem_decode(<<"-----BEGIN EC PRIVATE KEY-----
1> MHcCAQEEIGpSqNErkMHbjdeBQBI6NdlK8QgFluCJvGhkt3g5n5zboAoGCCqGSM49
1> AwEHoUQDQgAE43/FB66JPZ2JCbL+e7b4TVpDJAOeeaHmy7NPYL1cOVJFRBux91M4
1> QYBu1s5DigaQdi/Qz7KK/Yr5EMktgulXHA==
1> -----END EC PRIVATE KEY-----">>)),
1>
1> PrivateKey = public_key:pem_entry_decode(PrivatePem),
1> Signature = public_key:sign(Message, sha256, PrivateKey).
<<48,68,2,32,64,80,146,169,96,232,174,140,196,59,46,54,
107,199,145,184,86,181,79,168,165,107,54,157,222,...>>
你可以这样验证:
2> PublicPem = lists:nth(1, public_key:pem_decode(<<"-----BEGIN PUBLIC KEY-----
2> MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE43/FB66JPZ2JCbL+e7b4TVpDJAOe
2> eaHmy7NPYL1cOVJFRBux91M4QYBu1s5DigaQdi/Qz7KK/Yr5EMktgulXHA==
2> -----END PUBLIC KEY-----">>)),
2>
2> PublicKey = public_key:pem_entry_decode(PublicPem),
2> VerifyResult = public_key:verify(Message, sha256, Signature, PublicKey),
2> VerifyResult.
true