不喜欢广告? 无广告 今天

BIP39 密钥短语 这12个词实际上是什么,以及HD钱包派生的工作原理

发布日期

一个BIP39助记词是将熵编码为单词——这是钱包恢复背后的密码学结构。本指南详细解释了单词列表的数学原理、PBKDF2种子步骤、BIP44派生路径、钱包为何对地址存在分歧,以及开发者需要了解的安全故障模式。

BIP39 密钥短及:这12个词实际上是什么,以及HD钱包派生方式 1
广告 移除?

一个BIP39助记词是128位或256位的随机数据,使用一个固定的2048词列表编码成人类可读的单词。这就是全部的技巧。所谓的“魔法”只是单词比十六进制字符串更容易写在纸上——从密码学角度看,这些单词本身并没有特殊之处。

有趣的是,它建立在该编码之上的四层堆栈:BIP39(单词列表和编码)、BIP32(分层确定性密钥派生)、BIP43(目的字段约定)和BIP44(币/账户/地址结构)。大多数解释将这四层混为一谈。这个解释将它们分开。

单词列表:每个单词11位,包含校验码

BIP39英文单词列表 正好有2048个单词。211 = 2048,因此每个单词编码11位信息。一个12词短语总共携带132位信息;一个24词短语携带264位信息。

并非所有这些位都是熵。最后一个单词的最后几位是校验码——即SHA256(熵字节)的前ENT/32位,其中ENT是熵的长度(以位为单位):

短语长度熵位(ENT)校验码位(CS = ENT/32)总位数
12个词1284132
15个词1605165
18个词1926198
21个词2247231
24个词2568264

校验码是“几乎有效”助记词失败的原因。翻转一个熵位会使校验字节改变,从而使短语无效。这可以检测到输入错误——特别是那些落在校验码位上的错误。在派生密钥之前验证短语的钱包会完全拒绝该短语。有些钱包不进行验证,而是默默地从垃圾熵中派生一个钱包。

Python中的熵到单词映射:

import hashlib, os

# Generate 128 bits of entropy
entropy = os.urandom(16)  # 16 bytes = 128 bits

# Compute checksum: first 4 bits of SHA256(entropy)
h = hashlib.sha256(entropy).digest()
checksum_bits = format(h[0], '08b')[:4]  # first 4 bits of SHA256 output

# Combine entropy bits + checksum bits
all_bits = format(int.from_bytes(entropy, 'big'), '0128b') + checksum_bits
# all_bits is now 132 bits

# Split into 11-bit groups -> 12 word indices (0-2047)
word_indices = [int(all_bits[i:i+11], 2) for i in range(0, 132, 11)]
# Look up each index in the BIP39 word list to get the mnemonic

助记词不是密钥:PBKDF2步骤

这是大多数解释出错的地方。12个单词不是你的私钥,也不是直接用于签名。它们是中间编码。在任何密钥材料派生之前,助记词会通过PBKDF2-HMAC-SHA512进行扩展:

import hashlib

mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
passphrase = ""  # optional; empty string = no passphrase

seed = hashlib.pbkdf2_hmac(
    'sha512',
    mnemonic.encode('utf-8'),                    # password: the mnemonic words
    ('mnemonic' + passphrase).encode('utf-8'),   # salt: always "mnemonic" + passphrase
    2048,                                         # iterations
    dklen=64                                      # 512 bits output
)
# seed is 64 bytes (512 bits) -- this is what actually seeds key derivation

需要注意的两点:

  • 盐总是字面字符串 "mnemonic" 与密码短语连接。空密码短语意味着盐就是 "mnemonic" ——没有单独的“无密码短语”模式。
  • 密码短语会完全改变最终的种子。相同的12个单词加上密码短语 "A" vs "a" 会产生完全不同的钱包和完全不同的地址。这就是“第25个词”功能:密码短语让你保持合理的否认能力(一个短语对应两个钱包),但一旦丢失密码短语,即使拥有12个单词也无法恢复。

从种子到主密钥:BIP32

512位种子通过HMAC-SHA512与固定密钥字符串 "Bitcoin seed"进行运算。64字节的输出分为两个256位部分:

import hmac, hashlib

master = hmac.new(b'Bitcoin seed', seed, hashlib.sha512).digest()
master_private_key = master[:32]   # left 256 bits: the actual EC private key
master_chain_code  = master[32:]   # right 256 bits: used for all child derivation

链码是HD钱包能够工作的关键:它防止了即使已知父密钥,也能暴力破解派生密钥。如果没有链码,仅知道父公钥和任意子私钥,就会泄露所有兄弟私钥。有了链码,子密钥派生需要父密钥和链码作为输入——对于强化派生,还需要父私钥。

共同 master_private_key + master_chain_code 构成主扩展私钥,编码为一个 xprv... Base58Check字符串。相应的扩展公钥是 xpub... ——对需要派生地址但不暴露私钥材料的只读钱包非常有用。

BIP44派生路径:m/44’/60’/0’/0/0 解码

BIP44定义了一个五层派生层次结构。以下是 m/44’/60’/0’/0/0 ——标准以太坊第一个地址路径——逐层分解:

等级价值十六进制索引意义
m主密钥根
44'目的0x8000002CBIP44目的。强化派生(撇号 = 0x80000000 + 索引)。
60'币类型0x8000003C以太坊,根据 SLIP-0044。比特币 = 0′,Solana = 501′,等等。
0'账户0x80000000第一个账户。为独立账户递增。
0变更0外部链(0 = 接收地址,1 = 变更地址)。钱包很少在以太坊链上使用变更链。
0索引0地址索引。为第二个地址、第三个地址等递增。

撇号表示 强化派生。强化子密钥只能从父私钥派生,而不能从父公钥派生。这一点很重要,因为使用普通(非强化)派生时,如果一个子私钥被泄露,加上父公钥,就能暴露父私钥和所有兄弟密钥。在目的、币类型和账户层级上,强化派生是标准。

为什么相同的短语在不同钱包中会产生不同的地址

短语和钱包的默认路径是独立变量。钱包历史上对路径存在分歧,有些分歧至今仍未解决。

对于以太坊,主要分歧:

  • MetaMask、Ledger Live、大多数现代钱包: m/44'/60'/0'/0/N ——标准BIP44。
  • MyEtherWallet(旧版默认): m/44'/60'/0'/N ——少一个层级。从相同短语在MEW中使用旧路径恢复的钱包,其地址集与MetaMask完全不同。
  • Trezor Suite: 现在遵循标准BIP44用于ETH,但历史上在一些替代币上存在自己的特殊规则。

对于比特币,分歧是结构性的:BIP44(m/44'/0'/0',旧式P2PKH,地址以1开头),BIP49(m/49'/0'/0',P2SH-P2WPKH,地址以3开头),和BIP84(m/84'/0'/0',原生segwit P2WPKH,地址以bc1q开头)都从相同的种子生成不同的地址。地址格式告诉你使用了哪条路径,这也是在排查恢复问题时地址格式重要的原因。

如果你导入一个短语后“资金不存在”,请先检查派生路径,再假设短语错误。大多数钱包在高级导入时允许你手动指定路径。

安全故障模式

这里的密码学并不是薄弱环节。所有针对BIP39钱包的攻击都针对人类环节。

钓鱼:输入您的恢复短语以继续

这是最高频的攻击,占显著比例。虚假钱包界面、浏览器扩展被恶意脚本注入、Discord中的假冒客服——都要求用户在某个地方输入他们的12个单词。正确的思维模型:合法的钱包软件在初始设置后永远不会要求输入种子短语。只要请求短语,就是攻击,无论界面看起来多可信。

剪贴板监控

恶意软件会轮询剪贴板,检测到12或24个已知BIP39单词序列,并进行窃取。窃取窗口仅为毫秒级。将种子短语复制到任何地方——即使只是短暂地粘贴到文本编辑器——都会造成暴露。剪贴板历史管理器(Windows剪贴板历史、macOS剪贴板管理器、IDE粘贴历史)尤其高风险。

截图和云照片同步

在手机上截图种子短语后,几秒钟内就会上传到iCloud照片或Google照片,大多数人还没来得及读完。这些备份在客户端未加密。‘我截图是为了安全保存’就是一条直接的泄露路径。将纸备份存放在物理安全位置,绝不是玩笑。

服务器端助记词生成

任何在服务器端生成BIP39短语的网站都拥有熵的副本。唯一安全的生成位置是在你控制的设备上,且在生成时是离线的。硬件钱包、空气隔离的机器或经过审计的客户端工具。网站不符合这些条件——即使JavaScript代码看起来正确,你也无法验证服务器没有记录输出。

如果你需要检查或验证短语的结构——检查熵、查看派生种子、验证路径——则 BIP39 助记符转换器 完全在浏览器中运行。短语永远不会离开你的机器,这是唯一安全的架构。

开发者角度:你几乎肯定不应该在你的应用中处理种子短语

如果你正在开发一个与加密相关的应用,将“在后端处理种子短语”的直觉几乎总是错误的。攻击面:

  • 日志记录。 每个Web框架都会在某个地方记录请求体。一条调试信息、一个配置错误的日志级别,就会导致所有经过API的短语都保存在磁盘上——永久存在,分布在多个你未审计的日志目标中。
  • 传输。 HTTPS保护了网络传输。它不保护负载均衡器、后端进程内存、数据库或日志聚合器。每个都是独立的漏洞面。
  • 内存。 进程转储、崩溃报告、核心文件和堆快照会捕获内存中的字符串。一个种子短语在Python字典或JavaScript对象中并不等于零拷贝;它很可能在被“删除”前出现在多个分配中。
  • 责任。 如果你的后端处理了用户种子短语,一旦被入侵,损害将是永久的。与密码不同,没有重置机制。受影响的用户会永久损失资金,且无任何救济途径。

真正有效的架构:在客户端派生所需内容,并仅传输输出——公钥、只读xpub或签名交易。后端永远不会看到短语。正确实现这一功能的库: @scure/bip39 (经过审计,依赖项最少), ethers.js,并且 bitcoinjs-lib。对于硬件钱包集成,Trezor和Ledger SDK返回签名交易——密钥永远不会离开设备。

完整的派生链概览

输入手术输出
1. 熵128–256位随机位SHA256校验码 → 11位组12–24词助记词
2. 种子助记词单词 + “mnemonic” + 密码短语PBKDF2-HMAC-SHA516,2048轮512位种子(64字节)
3. 主密钥种子字节HMAC-SHA512(“Bitcoin seed”, 种子)主私钥(256位)+ 链码(256位)
4. 账户密钥主密钥 + 路径 m/44’/60’/0’BIP32强化子密钥派生 × 3账户扩展私钥
5. 地址密钥账户密钥 + 路径 /0/NBIP32普通子密钥派生 × 2子私钥 → secp256k1公钥 → keccak256 → 地址

BIP39 正好做到了它设计的目的:让熵变得可写且可恢复。攻击面并不在密码学上——PBKDF2 2048轮、HMAC-SHA512、secp256k1 都是安全的。攻击完全是操作层面的:在不该的地方输入短语、将短语数字化存储、信任一个服务器端生成工具。数学是正确的。人类是薄弱环节,因此开发者建议是“设计系统,确保短语永远不会触碰你的基础设施。”

想要享受无广告的体验吗? 立即无广告

安装我们的扩展

将 IO 工具添加到您最喜欢的浏览器,以便即时访问和更快地搜索

添加 Chrome 扩展程序 添加 边缘延伸 添加 Firefox 扩展 添加 Opera 扩展

记分板已到达!

记分板 是一种有趣的跟踪您游戏的方式,所有数据都存储在您的浏览器中。更多功能即将推出!

广告 移除?
广告 移除?
广告 移除?

新闻角 包含技术亮点

参与其中

帮助我们继续提供有价值的免费工具

给我买杯咖啡
广告 移除?