Python中如何基于签名和消息推导ECDSA公钥 适配SECP256k1曲线
实现方案
ECDSA 签名本质是(r, s)二元组,仅靠(r,s)和原始消息最多可以推导出4个候选公钥,需要额外的*恢复标识符(recovery id,取值0/1/2/3)*才能唯一确定正确公钥。如果没有提前保存恢复id,也可以遍历所有候选公钥,通过验签逻辑筛选出正确的那一个。
方案1:使用原生ecdsa库实现
无需额外安装其他依赖,和你现有代码的兼容性最好:
from ecdsa import SigningKey, SECP256k1, VerifyingKey from ecdsa.util import sigdecode_string import hashlib def vk_from_signature(signature: bytes, msg: bytes, curve=SECP256k1, hashfunc=hashlib.sha256) -> VerifyingKey: # 计算消息哈希,必须和签名时使用的哈希算法完全一致 msg_hash = hashfunc(msg).digest() r, s = sigdecode_string(signature, curve.order) # 遍历所有可能的恢复标识符 for recid in range(4): try: vk = VerifyingKey.from_public_key_recovery( signature, msg_hash, curve=curve, recid=recid, hashfunc=hashfunc ) # 验签确认公钥正确性 if vk.verify(signature, msg, hashfunc=hashfunc): return vk except: continue raise ValueError("无法从给定签名和消息恢复出有效公钥") # 原有签名逻辑调整,统一哈希算法避免歧义 sk = SigningKey.generate(curve=SECP256k1) vk = sk.verifying_key msg = b"Some arbitrary message" signature = sk.sign(msg, hashfunc=hashlib.sha256) print("原始公钥: ", vk.to_string().hex()) # 恢复公钥 vk2 = vk_from_signature(signature, msg) print("恢复得到的公钥: ", vk2.to_string().hex()) if vk.to_string().hex() == vk2.to_string().hex(): print("SUCCESS")
方案2:使用coincurve库实现(仅针对SECP256k1曲线)
如果你只需要用到SECP256k1曲线,这个专门优化过的库效率更高,实现更简单:
- 先安装依赖:
pip install coincurve - 代码实现:
from coincurve import PrivateKey, PublicKey # 签名逻辑 sk = PrivateKey() vk = sk.public_key msg = b"Some arbitrary message" # 生成带恢复标识符的签名(65字节,最后1字节为恢复id) signature_with_recid = sk.sign_recoverable(msg) # 公钥恢复逻辑 def vk_from_signature(signature: bytes, msg: bytes) -> PublicKey: return PublicKey.from_signature_and_message(signature, msg, hasher=None) # 测试 vk2 = vk_from_signature(signature_with_recid, msg) print("原始公钥: ", vk.format().hex()) print("恢复得到的公钥: ", vk2.format().hex()) if vk.format() == vk2.format(): print("SUCCESS")
内容的提问来源于stack exchange,提问作者Shatnerz
相关产品推荐
相关产品推荐

