如何使用Python的bitcoinlib库对原始文本消息进行签名并生成Base64编码的有效签名?
如何使用Python的bitcoinlib库对原始文本消息进行签名并生成Base64编码的有效签名?
你遇到的问题其实很典型——bitcoinlib确实把主要精力放在了比特币交易处理上,直接用Transaction类来签消息完全走偏了,它根本不是干这个的!咱们换个思路,利用bitcoinlib的Key类底层的ECDSA能力,手动实现比特币标准的消息签名流程就行。
先说说你原来代码的问题
你之前误用了Transaction类:
Transaction.inputs_add()是用来添加交易输入的,和消息签名完全不相关,自然会报错;Transaction.sign()的参数是为交易签名设计的,根本不支持传入自定义消息,所以这条路走不通。
正确解决方案:基于比特币标准手动实现
比特币的消息签名有一套固定规范,我们可以基于Key类的底层能力来实现:
- 按照比特币要求构造待签名的消息payload(必须加上特定前缀和消息长度,防止签名被恶意复用在其他场景);
- 对payload做双重SHA256哈希;
- 用私钥对哈希值签名;
- 把签名结果转成Base64编码。
修改后的完整代码
from bitcoinlib.keys import Key import hashlib import base64 class BitcoinClient: def __init__(self, private_key_wif: str) -> None: """ Initialize the BitcoinClient with a WIF private key. :param private_key_wif: The private key in Wallet Import Format (WIF). """ self.key = Key(import_key=private_key_wif) self.address = self.key.address() def get_address(self) -> str: """ Get the Bitcoin address associated with the private key. :return: Bitcoin address as a string. """ return self.address def sign_message(self, message: str) -> str: """ Sign a message using the private key, following Bitcoin's message signing standard. :param message: The message to be signed. :return: The signature as a base64-encoded string. """ # 将消息转为UTF-8字节 message_bytes = message.encode('utf-8') # 构造比特币标准的消息前缀+内容 prefix = "Bitcoin Signed Message:\n" payload = f"{prefix}{len(message_bytes)}\n{message}".encode('utf-8') # 计算双重SHA256哈希 double_hash = hashlib.sha256(hashlib.sha256(payload).digest()).digest() # 用私钥签名哈希(use_hash=False表示我们已经自己算好哈希了) signature_bytes = self.key.sign(double_hash, use_hash=False) # 转成Base64编码返回 return base64.b64encode(signature_bytes).decode('utf-8') def verify_message(self, message: str, signature: str) -> bool: """ Verify a signed message against the Bitcoin address. :param message: The original message. :param signature: The base64-encoded signature to verify. :return: True if the signature is valid, False otherwise. """ message_bytes = message.encode('utf-8') prefix = "Bitcoin Signed Message:\n" payload = f"{prefix}{len(message_bytes)}\n{message}".encode('utf-8') double_hash = hashlib.sha256(hashlib.sha256(payload).digest()).digest() # 解码Base64签名 signature_bytes = base64.b64decode(signature) # 验证签名 return self.key.verify(double_hash, signature_bytes, use_hash=False) if __name__ == "__main__": # 填入你的WIF私钥和测试消息 private_key_wif = "你的WIF私钥" message = "Hello world!" try: signer = BitcoinClient(private_key_wif) signature = signer.sign_message(message) address = signer.get_address() is_valid = signer.verify_message(message, signature) print(f"Bitcoin Address: {address}") print(f"Signature: {signature}") print(f"Is signature valid: {is_valid}") except ValueError as e: print(f"Error: {e}")
关键要点说明
- 我们完全抛弃了
Transaction类,直接用Key类的sign和verify方法,这两个方法可以直接处理原始哈希值; - 设置
use_hash=False非常重要:bitcoinlib的sign方法默认会对输入数据做哈希,而我们已经按照比特币标准计算了双重SHA256哈希,所以必须跳过内置的哈希步骤; - 构造payload时严格遵循比特币规范,这样生成的签名可以被其他比特币工具(比如Bitcoin Core钱包)验证。
补充说明
如果你未来升级bitcoinlib到更高版本,可能会发现Key类已经内置了sign_message和verify_message方法,到时候直接调用就行,但在0.6.0版本里,手动实现是最可靠的方案。
备注:内容来源于stack exchange,提问作者Sorry_my_code_is_dumb




