python-ecdsa 签名大小是否正确?
Is python-ecdsa signature size correct?
在比特币维基上我发现比特币使用ECDSA算法,Secp256k1曲线。
相关链接:
- https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
- https://en.bitcoin.it/wiki/Secp256k1
第一个link说私钥应该是32字节,public密钥是64字节,签名一般在71-73字节之间。说签名可以小概率小
然而,当我运行以下python3代码
>>> from ecdsa import SigningKey, SECP256k1
>>> private_key = SigningKey.generate(curve=SECP256k1)
>>> public_key = private_key.get_verifying_key()
>>> signature = private_key.sign(b'message')
>>> print((len(private_key.to_string()), len(public_key.to_string()), len(signature)))
我得到 (32, 64, 64) 作为输出。我希望得到类似 (32, 64, 72) 的结果。
我认为正在发生以下情况之一:
- 我误解了维基文章。
- 我使用 python-ecdsa 不正确
- 比特币 wiki 不正确
- python-ecdsa没有正确实现
前两个可能性较大。
任何人都可以向我解释为什么我的期望与实际得到的不匹配吗?
ECDSA 签名由两个数字组成,r 和 s 是 [1..n-1] 范围内的数字,其中 n 是曲线的阶数。 n 是 [2^(k-1)..2^k-1] 范围内的(已知)数字,其中 k 是密钥大小。所以 r 和 s 的大小通常与密钥大小(以字节为单位)相同,有时略小。
现在r和s可以有多种编码方式,其中常用的有两种:
- r 和 s 在 ASN.1 SEQUENCE 中被 DER 编码为两个 ASN.1 签名的 INTEGER 类型。
- r 和 s 被编码为两个静态大小的无符号整数,其大小与密钥大小(或顺序)相同八位字节或字节。
所以大小的差异只是因为值 r 和 s 的编码不同。当然,你需要先知道编码类型,然后才能验证签名。
由于 r 和 s 完全相同,独立于编码,因此在两个版本之间进行转换相对简单(如果您可以调用任何需要生成或解析 DER 编码的 ASN.1 结构的内容 "simple").
类型 1 已在 ANSI X9.62 中标准化,类型 2 通常称为平面编码,常用于嵌入式平台或智能卡。
r 和 s 非常 可能 与 n / 密钥大小相同,但原则上,它们可以是例如数字 3。发生这种情况的可能性非常小。但是,您应该 不 对 r 和 s 的大小执行任何测试。如果它们中的任何一个小于 8 个字节,那么您可能会开始挠头,因为发生这种情况的可能性在 1/2^63 和 1/2^64 之间,即 非常 不太可能。
所以:
- 我误解了维基文章。
不,wiki 文章采用 ANSI X9.62 的标准化编码。
- 我使用 python-ecdsa 不正确
不,python-ecdsa 包只是使用了不同的编码,你会感到惊讶。
- 比特币 wiki 不正确
不,比特币 wiki 假定为其协议选择了特定的编码。
- python-ecdsa没有正确实现
绝对不是;至少在签名的大小方面不是。
现在介绍实施细节;以下内容在文档中:
There are also multiple ways to represent a signature. The default sk.sign()
and vk.verify()
methods present it as a short string, for simplicity and minimal overhead. To use a different scheme, use the sk.sign(sigencode=) and vk.verify(sigdecode=) arguments. There are helper functions in the "ecdsa.util" module that can be useful here.
所以尝试使用sigencode=sigencode_der
来获得wiki文章所期望的格式。 util.py
来源包含您可能需要的所有转换。它使用 number_to_string
来创建静态大小的数字。此函数在 PKCS#1 (RSA) 中也称为 I2OSP 或整数到八位字节字符串原语。请注意,代码中的 "strings" 指的是 八位字节字符串 ,也称为 字节数组 - 而不是文本字符串。
python-ecdsa 的唯一问题是性能,因为它太慢了。
更好的库:starkbank-ecdsa
如何安装:
pip install starkbank-ecdsa
使用方法:
# Generate Keys
privateKey = PrivateKey()
publicKey = privateKey.publicKey()
message = "My test message"
# Generate Signature
signature = Ecdsa.sign(message, privateKey)
# Verify if signature is valid
print Ecdsa.verify(message, signature, publicKey)
在比特币维基上我发现比特币使用ECDSA算法,Secp256k1曲线。
相关链接:
- https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
- https://en.bitcoin.it/wiki/Secp256k1
第一个link说私钥应该是32字节,public密钥是64字节,签名一般在71-73字节之间。说签名可以小概率小
然而,当我运行以下python3代码
>>> from ecdsa import SigningKey, SECP256k1
>>> private_key = SigningKey.generate(curve=SECP256k1)
>>> public_key = private_key.get_verifying_key()
>>> signature = private_key.sign(b'message')
>>> print((len(private_key.to_string()), len(public_key.to_string()), len(signature)))
我得到 (32, 64, 64) 作为输出。我希望得到类似 (32, 64, 72) 的结果。
我认为正在发生以下情况之一:
- 我误解了维基文章。
- 我使用 python-ecdsa 不正确
- 比特币 wiki 不正确
- python-ecdsa没有正确实现
前两个可能性较大。
任何人都可以向我解释为什么我的期望与实际得到的不匹配吗?
ECDSA 签名由两个数字组成,r 和 s 是 [1..n-1] 范围内的数字,其中 n 是曲线的阶数。 n 是 [2^(k-1)..2^k-1] 范围内的(已知)数字,其中 k 是密钥大小。所以 r 和 s 的大小通常与密钥大小(以字节为单位)相同,有时略小。
现在r和s可以有多种编码方式,其中常用的有两种:
- r 和 s 在 ASN.1 SEQUENCE 中被 DER 编码为两个 ASN.1 签名的 INTEGER 类型。
- r 和 s 被编码为两个静态大小的无符号整数,其大小与密钥大小(或顺序)相同八位字节或字节。
所以大小的差异只是因为值 r 和 s 的编码不同。当然,你需要先知道编码类型,然后才能验证签名。
由于 r 和 s 完全相同,独立于编码,因此在两个版本之间进行转换相对简单(如果您可以调用任何需要生成或解析 DER 编码的 ASN.1 结构的内容 "simple").
类型 1 已在 ANSI X9.62 中标准化,类型 2 通常称为平面编码,常用于嵌入式平台或智能卡。
r 和 s 非常 可能 与 n / 密钥大小相同,但原则上,它们可以是例如数字 3。发生这种情况的可能性非常小。但是,您应该 不 对 r 和 s 的大小执行任何测试。如果它们中的任何一个小于 8 个字节,那么您可能会开始挠头,因为发生这种情况的可能性在 1/2^63 和 1/2^64 之间,即 非常 不太可能。
所以:
- 我误解了维基文章。
不,wiki 文章采用 ANSI X9.62 的标准化编码。
- 我使用 python-ecdsa 不正确
不,python-ecdsa 包只是使用了不同的编码,你会感到惊讶。
- 比特币 wiki 不正确
不,比特币 wiki 假定为其协议选择了特定的编码。
- python-ecdsa没有正确实现
绝对不是;至少在签名的大小方面不是。
现在介绍实施细节;以下内容在文档中:
There are also multiple ways to represent a signature. The default
sk.sign()
andvk.verify()
methods present it as a short string, for simplicity and minimal overhead. To use a different scheme, use the sk.sign(sigencode=) and vk.verify(sigdecode=) arguments. There are helper functions in the "ecdsa.util" module that can be useful here.
所以尝试使用sigencode=sigencode_der
来获得wiki文章所期望的格式。 util.py
来源包含您可能需要的所有转换。它使用 number_to_string
来创建静态大小的数字。此函数在 PKCS#1 (RSA) 中也称为 I2OSP 或整数到八位字节字符串原语。请注意,代码中的 "strings" 指的是 八位字节字符串 ,也称为 字节数组 - 而不是文本字符串。
python-ecdsa 的唯一问题是性能,因为它太慢了。
更好的库:starkbank-ecdsa
如何安装:
pip install starkbank-ecdsa
使用方法:
# Generate Keys
privateKey = PrivateKey()
publicKey = privateKey.publicKey()
message = "My test message"
# Generate Signature
signature = Ecdsa.sign(message, privateKey)
# Verify if signature is valid
print Ecdsa.verify(message, signature, publicKey)