Python ecdsa库secp256r1签名如何获取v(恢复ID)字节?
解决Python ecdsa库生成secp256r1 65字节[r,s,v]签名的问题
核心思路
要生成符合要求的65字节签名,需从sign_deterministic返回的签名对象中提取r、s,再手动计算归一化后的s和恢复ID v(0-3),完全对齐你提供的Rust fastcrypto逻辑。
实现步骤
- 导入依赖库
import hashlib import ecdsa from ecdsa.curves import SECP256r1 from ecdsa.util import sigdecode_string from ecdsa import rfc6979
- 生成密钥对与签名对象
# 生成secp256r1密钥对 private_key = ecdsa.SigningKey.generate(curve=SECP256r1) public_key = private_key.get_verifying_key() # 待签名数据与哈希 data = b"your_transaction_data_here" hash_data = hashlib.sha256(data).digest() # 获取签名对象(而非直接转64字节) sig_obj = private_key.sign_deterministic(hash_data, hashfunc=hashlib.sha256) # 解码签名得到原始r、s值 r, s = sigdecode_string(sig_obj, SECP256r1.order)
- 归一化s值
secp256r1要求s必须落在曲线阶的下半部分(≤n/2),若原始s超出范围则转为n - s:
n = SECP256r1.order s_high = s > n // 2 s_normalized = n - s if s_high else s
- 计算恢复ID v
对应Rust代码逻辑,先获取签名生成时临时点k*G的y坐标奇偶性,再结合s是否高位的异或结果,最终得到v(0-3):
# 用RFC6979生成确定性签名的k值 k = rfc6979.generate_k(n, private_key.privkey.secret_multiplier, hashlib.sha256, hash_data) # 计算k*G的y坐标奇偶性 point = SECP256r1.generator * k y_odd = point.y() % 2 == 1 # 按Rust逻辑计算is_y_odd is_y_odd = y_odd ^ s_high # 确定v值:s高位时v加2,否则直接取is_y_odd v = is_y_odd + 2 if s_high else is_y_odd
- 拼接65字节签名
将r、归一化后的s、v分别转为字节并拼接:
r_bytes = r.to_bytes(32, byteorder='big') s_bytes = s_normalized.to_bytes(32, byteorder='big') v_bytes = bytes([v]) signature_65 = r_bytes + s_bytes + v_bytes
关键说明
- 恢复ID v的取值0-3对应两种场景:s是否需要归一化(加2标识)、临时点y坐标的奇偶性(0/1),完全匹配你提供的Rust逻辑。
- 全程未使用EIP-155链ID计算v,符合区块链规范要求。
内容的提问来源于stack exchange,提问作者Frank C.
相关产品推荐
相关产品推荐

