升级到 OTP 18 会中断 public_key 库的使用
Upgrade to OTP 18 breaks usage of public_key library
在 Elixir 中构建 pem 文件需要几个步骤,包括构建实体。在 OTP 17 中,以下工作:
{public, private} = :crypto.generate_key(:ecdh, :secp256k1)
ec_entity = {:ECPrivateKey,
1,
:binary.bin_to_list(private),
{:namedCurve, {1, 3, 132, 0, 10}},
{0, public}}
der_encoded = :public_key.der_encode(:ECPrivateKey, ec_entity)
pem = public_key.pem_encode([{:ECPrivateKey, der_encoded, :not_encrypted}])
但是使用OTP 18,出现如下错误:
{public, private} = :crypto.generate_key(:ecdh, :secp256k1)
ec_entity = {:ECPrivateKey,
1,
:binary.bin_to_list(private),
{:namedCurve, {1, 3, 132, 0, 10}},
{0, public}}
der_encoded = :public_key.der_encode(:ECPrivateKey, ec_entity)
** (MatchError) no match of right hand side value: {:error, {:asn1, :badarg}}
public_key.erl:253: :public_key.der_encode/2
此错误的来源是什么?
错误的根源是 public_key 实体在 OTP 17 和 OTP 18 之间的构造方式发生了变化。如果我们反转这个过程,从 pem 文件开始,我们可以看到差异.
一次性密码 17:
iex(6)> pem = "-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIJniJF4vtTqE4wS5AkhmMZsHIbil0l3XfRButkw5IJYFoAcGBSuBBAAK\noUQDQgAEtxm+jijBB0JxZTceHnCHE0HpMXJp1ScVUZ5McvDUVsS/Dek8IdAsMOPz\nnnVALflZzXtH/wU9p2LrFdJeuXwL8g==\n-----END EC PRIVATE KEY-----\n\n"
"-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIJniJF4vtTqE4wS5AkhmMZsHIbil0l3XfRButkw5IJYFoAcGBSuBBAAK\noUQDQgAEtxm+jijBB0JxZTceHnCHE0HpMXJp1ScVUZ5McvDUVsS/Dek8IdAsMOPz\nnnVALflZzXtH/wU9p2LrFdJeuXwL8g==\n-----END EC PRIVATE KEY-----\n\n"
iex(7)> [{type, decoded, _}] = :public_key.pem_decode(pem)
[{:ECPrivateKey,
<<48, 116, 2, 1, 1, 4, 32, 153, 226, 36, 94, 47, 181, 58, 132, 227, 4, 185, 2, 72, 102, 49, 155, 7, 33, 184, 165, 210, 93, 215, 125, 16, 110, 182, 76, 57, 32, 150, 5, 160, 7, 6, 5, 43, 129, 4, 0, 10, ...>>,
:not_encrypted}]
iex(8)> :public_key.der_decode(type, decoded)
{:ECPrivateKey, 1,
[153, 226, 36, 94, 47, 181, 58, 132, 227, 4, 185, 2, 72, 102, 49, 155, 7, 33,
184, 165, 210, 93, 215, 125, 16, 110, 182, 76, 57, 32, 150, 5],
{:namedCurve, {1, 3, 132, 0, 10}},
{0,
<<4, 183, 25, 190, 142, 40, 193, 7, 66, 113, 101, 55, 30, 30, 112, 135, 19, 65, 233, 49, 114, 105, 213, 39, 21, 81, 158, 76, 114, 240, 212, 86, 196, 191, 13, 233, 60, 33, 208, 44, 48, 227, 243, 158, 117, ...>>}}
一次性密码 18:
iex(5)> [{type, decoded, _}] = :public_key.pem_decode(pem)
[{:ECPrivateKey,
<<48, 116, 2, 1, 1, 4, 32, 153, 226, 36, 94, 47, 181, 58, 132, 227, 4, 185, 2, 72, 102, 49, 155, 7, 33, 184, 165, 210, 93, 215, 125, 16, 110, 182, 76, 57, 32, 150, 5, 160, 7, 6, 5, 43, 129, 4, 0, 10, ...>>,
:not_encrypted}]
iex(6)> entity = :public_key.der_decode(type, decoded)
{:ECPrivateKey, 1,
<<153, 226, 36, 94, 47, 181, 58, 132, 227, 4, 185, 2, 72, 102, 49, 155, 7, 33, 184, 165, 210, 93, 215, 125, 16, 110, 182, 76, 57, 32, 150, 5>>,
{:namedCurve, {1, 3, 132, 0, 10}},
<<4, 183, 25, 190, 142, 40, 193, 7, 66, 113, 101, 55, 30, 30, 112, 135, 19, 65, 233, 49, 114, 105, 213, 39, 21, 81, 158, 76, 114, 240, 212, 86, 196, 191, 13, 233, 60, 33, 208, 44, 48, 227, 243, 158, 117, 64, ...>>}
区别在于 public 和私钥的表示方式。
ECPrivateKey 记录的签名是:
ECPrivateKey'{ version, privateKey, parameters, publicKey}
在 Erlang 18 中,两个值都以普通二进制表示,在 17 中,私钥是一个列表,public 密钥是元组 {0, binary}
的一部分。
因此,为了正确构建 pem 文件,必须更改实体表示。
{public, private} = :crypto.generate_key(:ecdh, :secp256k1)
entity = {:ECPrivateKey,
1,
private,
{:namedCurve, {1, 3, 132, 0, 10}},
public}
使用记录的新表示将解决问题。
我并没有真正检查为什么你的版本适用于某些版本,但我有一些适用于所有这些 erlang 版本的代码:19.0、18.2.1、18.1、18.0、17.5、R16B03 (运行 在特拉维斯上)。
-include_lib("public_key/include/public_key.hrl").
genPEMKey() ->
CurveId = secp256k1,
{PubKey, PrivKey} = crypto:generate_key(ecdh, CurveId),
Key = #'ECPrivateKey'{version = 1,
privateKey = PrivKey,
parameters = {
namedCurve,
pubkey_cert_records:namedCurves(CurveId)},
publicKey = PubKey},
DERKey = public_key:der_encode('ECPrivateKey', Key),
public_key:pem_encode([{'ECPrivateKey', DERKey, not_encrypted}]).
这段代码基于 OTP 代码库中的示例:
https://github.com/erlang/otp/blob/master/lib/public_key/test/erl_make_certs.erl#L407
在 Elixir 中构建 pem 文件需要几个步骤,包括构建实体。在 OTP 17 中,以下工作:
{public, private} = :crypto.generate_key(:ecdh, :secp256k1)
ec_entity = {:ECPrivateKey,
1,
:binary.bin_to_list(private),
{:namedCurve, {1, 3, 132, 0, 10}},
{0, public}}
der_encoded = :public_key.der_encode(:ECPrivateKey, ec_entity)
pem = public_key.pem_encode([{:ECPrivateKey, der_encoded, :not_encrypted}])
但是使用OTP 18,出现如下错误:
{public, private} = :crypto.generate_key(:ecdh, :secp256k1)
ec_entity = {:ECPrivateKey,
1,
:binary.bin_to_list(private),
{:namedCurve, {1, 3, 132, 0, 10}},
{0, public}}
der_encoded = :public_key.der_encode(:ECPrivateKey, ec_entity)
** (MatchError) no match of right hand side value: {:error, {:asn1, :badarg}}
public_key.erl:253: :public_key.der_encode/2
此错误的来源是什么?
错误的根源是 public_key 实体在 OTP 17 和 OTP 18 之间的构造方式发生了变化。如果我们反转这个过程,从 pem 文件开始,我们可以看到差异.
一次性密码 17:
iex(6)> pem = "-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIJniJF4vtTqE4wS5AkhmMZsHIbil0l3XfRButkw5IJYFoAcGBSuBBAAK\noUQDQgAEtxm+jijBB0JxZTceHnCHE0HpMXJp1ScVUZ5McvDUVsS/Dek8IdAsMOPz\nnnVALflZzXtH/wU9p2LrFdJeuXwL8g==\n-----END EC PRIVATE KEY-----\n\n"
"-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIJniJF4vtTqE4wS5AkhmMZsHIbil0l3XfRButkw5IJYFoAcGBSuBBAAK\noUQDQgAEtxm+jijBB0JxZTceHnCHE0HpMXJp1ScVUZ5McvDUVsS/Dek8IdAsMOPz\nnnVALflZzXtH/wU9p2LrFdJeuXwL8g==\n-----END EC PRIVATE KEY-----\n\n"
iex(7)> [{type, decoded, _}] = :public_key.pem_decode(pem)
[{:ECPrivateKey,
<<48, 116, 2, 1, 1, 4, 32, 153, 226, 36, 94, 47, 181, 58, 132, 227, 4, 185, 2, 72, 102, 49, 155, 7, 33, 184, 165, 210, 93, 215, 125, 16, 110, 182, 76, 57, 32, 150, 5, 160, 7, 6, 5, 43, 129, 4, 0, 10, ...>>,
:not_encrypted}]
iex(8)> :public_key.der_decode(type, decoded)
{:ECPrivateKey, 1,
[153, 226, 36, 94, 47, 181, 58, 132, 227, 4, 185, 2, 72, 102, 49, 155, 7, 33,
184, 165, 210, 93, 215, 125, 16, 110, 182, 76, 57, 32, 150, 5],
{:namedCurve, {1, 3, 132, 0, 10}},
{0,
<<4, 183, 25, 190, 142, 40, 193, 7, 66, 113, 101, 55, 30, 30, 112, 135, 19, 65, 233, 49, 114, 105, 213, 39, 21, 81, 158, 76, 114, 240, 212, 86, 196, 191, 13, 233, 60, 33, 208, 44, 48, 227, 243, 158, 117, ...>>}}
一次性密码 18:
iex(5)> [{type, decoded, _}] = :public_key.pem_decode(pem)
[{:ECPrivateKey,
<<48, 116, 2, 1, 1, 4, 32, 153, 226, 36, 94, 47, 181, 58, 132, 227, 4, 185, 2, 72, 102, 49, 155, 7, 33, 184, 165, 210, 93, 215, 125, 16, 110, 182, 76, 57, 32, 150, 5, 160, 7, 6, 5, 43, 129, 4, 0, 10, ...>>,
:not_encrypted}]
iex(6)> entity = :public_key.der_decode(type, decoded)
{:ECPrivateKey, 1,
<<153, 226, 36, 94, 47, 181, 58, 132, 227, 4, 185, 2, 72, 102, 49, 155, 7, 33, 184, 165, 210, 93, 215, 125, 16, 110, 182, 76, 57, 32, 150, 5>>,
{:namedCurve, {1, 3, 132, 0, 10}},
<<4, 183, 25, 190, 142, 40, 193, 7, 66, 113, 101, 55, 30, 30, 112, 135, 19, 65, 233, 49, 114, 105, 213, 39, 21, 81, 158, 76, 114, 240, 212, 86, 196, 191, 13, 233, 60, 33, 208, 44, 48, 227, 243, 158, 117, 64, ...>>}
区别在于 public 和私钥的表示方式。
ECPrivateKey 记录的签名是:
ECPrivateKey'{ version, privateKey, parameters, publicKey}
在 Erlang 18 中,两个值都以普通二进制表示,在 17 中,私钥是一个列表,public 密钥是元组 {0, binary}
的一部分。
因此,为了正确构建 pem 文件,必须更改实体表示。
{public, private} = :crypto.generate_key(:ecdh, :secp256k1)
entity = {:ECPrivateKey,
1,
private,
{:namedCurve, {1, 3, 132, 0, 10}},
public}
使用记录的新表示将解决问题。
我并没有真正检查为什么你的版本适用于某些版本,但我有一些适用于所有这些 erlang 版本的代码:19.0、18.2.1、18.1、18.0、17.5、R16B03 (运行 在特拉维斯上)。
-include_lib("public_key/include/public_key.hrl").
genPEMKey() ->
CurveId = secp256k1,
{PubKey, PrivKey} = crypto:generate_key(ecdh, CurveId),
Key = #'ECPrivateKey'{version = 1,
privateKey = PrivKey,
parameters = {
namedCurve,
pubkey_cert_records:namedCurves(CurveId)},
publicKey = PubKey},
DERKey = public_key:der_encode('ECPrivateKey', Key),
public_key:pem_encode([{'ECPrivateKey', DERKey, not_encrypted}]).
这段代码基于 OTP 代码库中的示例:
https://github.com/erlang/otp/blob/master/lib/public_key/test/erl_make_certs.erl#L407