You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确实现BIP32、BIP44等密钥派生?子密钥生成遇阻求助

比特币HD钱包子密钥派生问题

问题背景

我已成功生成父扩展密钥,但无法生成符合规范测试向量的正确子密钥。以下是能正常输出正确父密钥的代码:

import hashlib
import base58
import ecdsa

xprv = "0488ade4"
xpub = "0488b21e"

x = hmac.new("Bitcoin seed".encode('utf-8'), bytes.fromhex("000102030405060708090a0b0c0d0e0f"),
             hashlib.sha512).digest()
master_private_key = x[0:32]
master_chain_code = x[32:64]

x_private_key = bytes.fromhex(xprv) + \
    (bytes.fromhex("000000000000000000")) + master_chain_code + b'\x00' + master_private_key

double_hash = bytes.fromhex(hashlib.sha256(bytes.fromhex(hashlib.sha256(x_private_key).hexdigest())).hexdigest())
x_private_key = x_private_key + double_hash[0:4]

mpk = int.from_bytes(master_private_key, byteorder='big')
private_key = ecdsa.SigningKey.from_secret_exponent(mpk, curve=ecdsa.SECP256k1)
private_key_bytes = private_key.to_string()

verifying_key = private_key.get_verifying_key()
public_key_compressed_bytes = verifying_key.to_string("compressed")

x_public_key = bytes.fromhex(xpub) + \
    (b'\x00' * 9) + master_chain_code + bytes.fromhex(public_key_compressed_bytes.hex())

double_hash = bytes.fromhex(hashlib.sha256(bytes.fromhex(hashlib.sha256(x_public_key).hexdigest())).hexdigest())
x_public_key = x_public_key + double_hash[0:4]

print("x_private_key: ", base58.b58encode(x_private_key).decode())
print("x_public_key: ", base58.b58encode(x_public_key).decode())

子密钥派生尝试(未成功)

我尝试实现m/0路径的树形派生,但无法得到正确结果,代码如下:

i = 0
cx = hmac.new(master_chain_code, master_private_key + i.to_bytes(4, "big"), hashlib.sha512).digest()
cx_private_key = (cx[:32])
cx_chain_code = cx[32:]

h = hashlib.new('ripemd160')
h.update(hashlib.sha256(public_key_compressed_bytes).digest())
fingerprint = h.digest()

c_key = bytes.fromhex(xprv) + \
    b'\x01' + \
    fingerprint[:4] + \
    b'\x00\x00\x00\x00' + \
    cx_chain_code + \
    b'\x00' + \
    cx_private_key

double_hash = bytes.fromhex(hashlib.sha256(bytes.fromhex(hashlib.sha256(c_key).hexdigest())).hexdigest())
c_key = c_key + double_hash[0:4]
print("children_private_key: ", base58.b58encode(c_key).decode())

错误分析与修正方案

你的子密钥派生代码存在3个关键错误,修正后即可匹配测试向量:

1. HMAC输入错误

普通子密钥(索引值 < 2^31,即非hardened派生)的HMAC输入应为父公钥的压缩字节 + 子索引的4字节大端数据,而非父私钥。只有hardened子密钥(索引 >= 2^31)才使用父私钥作为HMAC输入。

2. 子私钥计算逻辑错误

子私钥不能直接使用cx[:32],必须将其转换为整数后与父私钥的整数形式相加,再对SECP256k1曲线的阶取模,公式为:

child_priv_int = (int.from_bytes(cx_left, 'big') + int.from_bytes(master_private_key, 'big')) % ecdsa.SECP256k1.order

3. 子索引字段硬编码问题

虽然m/0的索引字节确实是b'\x00\x00\x00\x00',但使用i.to_bytes(4, 'big')更通用,能避免索引变化时的硬编码错误。

修正后的完整代码

import hashlib
import base58
import ecdsa

# 先执行父密钥生成代码(复用之前的正确代码)
# ...

# 生成m/0路径的子私钥
i = 0  # 普通子密钥索引,<2^31
# 普通派生:HMAC输入为父公钥压缩字节 + 子索引
cx = hmac.new(master_chain_code, public_key_compressed_bytes + i.to_bytes(4, "big"), hashlib.sha512).digest()
cx_left = cx[:32]
cx_chain_code = cx[32:]

# 计算子私钥:模加运算
order = ecdsa.SECP256k1.order
child_priv_int = (int.from_bytes(cx_left, 'big') + int.from_bytes(master_private_key, 'big')) % order
child_priv_bytes = child_priv_int.to_bytes(32, 'big')  # 确保32字节长度,补前导零

# 父公钥指纹(前4字节)
h = hashlib.new('ripemd160')
h.update(hashlib.sha256(public_key_compressed_bytes).digest())
fingerprint = h.digest()[:4]

# 构建子扩展私钥结构
c_key = (
    bytes.fromhex(xprv) +
    b'\x01' +  # 深度:父密钥深度为0,子密钥深度为1
    fingerprint +
    i.to_bytes(4, 'big') +  # 子索引字节
    cx_chain_code +
    b'\x00' +  # 私钥前缀
    child_priv_bytes
)

# 计算并添加校验和
double_hash = hashlib.sha256(hashlib.sha256(c_key).digest()).digest()
c_key += double_hash[:4]

print("children_private_key: ", base58.b58encode(c_key).decode())

运行修正后的代码,即可得到符合测试向量的m/0路径子扩展私钥。

内容的提问来源于stack exchange,提问作者Dilettante

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 22:48:10