Bitcoin Core 钱包私钥加密机制:从 0.4.0 版发布说明到当前源码实现全解析
Bitcoin Core 0.4.0 版(2010 年)引入的原生钱包私钥加密是比特币客户端安全史上第一个里程碑式特性:它允许用户为钱包设置口令(passphrase),在发送比特币之前必须先解锁钱包。本文以 0.4.0 版发布说明 为核心,完整梳理该版本的主特性、已知兼容性问题与修复项,并对照当前仓库中的 加密上下文实现、钱包加密主流程 和 RPC 接口,说明这套"口令 → 主密钥 → 私钥"三层加密结构是如何一直沿用至今的。
一、0.4.0 版的核心发布内容
根据 release-notes-0.4.0.md,该版本的主特性是钱包私钥加密(Wallet Encryption):用户设置一句口令后,每次发送比特币前都必须输入该口令。发布说明给出了几条至今仍然成立的严肃警告:
- 口令即所有权:忘记或丢失口令意味着丢失钱包内全部比特币,"没有任何人,包括 Bitcoin 开发者在内,能够帮你恢复";
- 旧版客户端不兼容:早于 0.4 的 bitcoin 版本无法读取加密钱包,启动时直接崩溃;
- 加密前务必备份:官方建议先关闭客户端、拷贝
wallet.dat到安全位置,再执行加密,确认无误后删除该备份。
除了主特性,0.4.0 还包含两项值得记录的工程变更与修复:
| 变更项 | 说明 |
|---|---|
| Berkeley DB 升级 | 从 bdb 4.7 升级到 bdb 4.8。文档明确警告:升级到 0.4 后若再回退到旧版本,可能因 bdb 4.7 无法读取 bdb 4.8 的 "log" 文件而无法启动。这意味着 0.4 是单向升级点,加密钱包 + 新版 BDB 共同锁死了回退路径 |
| 多线程死锁修复 | 修复了多个导致 bitcoin 无响应(becomes-unresponsive)的多线程死锁 bug |
| 大交易数据库写入优化 | 针对输入(inputs)数量极多的交易优化数据库写入路径,文档将其定性为"修复了一个潜在的拒绝服务攻击"——即攻击者可构造大量输入的交易,使节点磁盘写入性能急剧劣化 |
发布说明还特别指出,钱包加密的技术细节参见源码树中的 doc/README(当前仓库中为 doc/README.md)。
二、加密的边界:只加密密钥,不加密整个钱包
0.4.0 发布说明中最关键的一段安全澄清是:
Bitcoin 内置的加密只加密发送比特币所需的实际密钥,而不是整个钱包。窃取钱包文件的人仍然能看到你所有的地址以及相关交易,你只是被保护免受他人花掉你的币。
这一点决定了钱包加密的真实安全语义:它防御的是"钱包文件被偷走后离线导出私钥、直接转走资金"这一场景,而不防御"交易历史与地址图谱泄露"。发布说明同时给出了完整的安全责任声明——一个稍具水平的钱包窃取木马只要再装一个键盘记录器,在你输入口令时把它记录下来,加密就形同虚设。官方列出的最低安全实践包括:
- 保持杀毒软件更新;
- 只在 Bitcoin 客户端内部输入钱包口令;
- 钱包口令不要与任何其他场景的口令相同(文档原话:"using the same passphrase only as your wallet passphrase")。
三、从发布说明到源码:三层密钥结构
0.4.0 文档描述的"口令加密钱包",在当前仓库中演化为一个三层结构,全部位于 src/wallet/ 目录下:
1. 口令 → 主密钥(CCrypter / CMasterKey)
crypter.h 的注释精确描述了这套算法(与 0.4 时代的 doc/README 一脉相承):
- 钱包持有一个 CMasterKey,包含随机盐(salt)和被口令加密的随机密钥(
vchCryptedKey); - 主密钥使用 AES-256-CBC 加密,加密所用的 key 由口令经
nDerivationMethod(0 ==EVP_sha512())与nDeriveIterations轮迭代派生; - 私钥再用 AES-256-CBC 加密,密钥为主密钥本身,IV 取公钥的 double-SHA256 值(见 crypter.h L28-L31)。
crypter.h 中几个至今未变的常量:
inline constexpr unsigned int WALLET_CRYPTO_KEY_SIZE = 32; // 256 位主密钥
inline constexpr unsigned int WALLET_CRYPTO_SALT_SIZE = 8; // 8 字节盐
inline constexpr unsigned int WALLET_CRYPTO_IV_SIZE = 16; // AES-256-CBC 的 16 字节 IV
口令到密钥的派生函数 BytesToKeySHA512AES(实现于 crypter.cpp)默认执行 25000 轮 SHA-512 迭代(CMasterKey::DEFAULT_DERIVE_ITERATIONS,crypter.h L48)。注释里保留着一段耐人寻味的历史:"25000 轮在一台 1.86 GHz Pentium M 上不到 0.1 秒,略低于我们愿意支持的最低端硬件"——这正是 2010 年前后的硬件画像,如今这一轮数已成为抗暴力破解的安全参数。
2. 主密钥 → 私钥(CScriptPubKeyManager)
每个私钥的加密点见 scriptpubkeyman.cpp:
if (!EncryptSecret(master_key, secret, pubkey.GetHash(), crypted_secret)) {
即调用 crypter.cpp L111 的 EncryptSecret,IV 参数传入 pubkey.GetHash()——与 crypter.h 头注释中"double-sha256 of the public key as the IV"的约定完全一致。密文随后以序列化形式写回钱包数据库,取代原来的明文私钥。
3. 加密主流程(CWallet::EncryptWallet)
wallet.cpp L843-L916 的 EncryptWallet 完整实现了发布说明所暗示的事务性流程:
- 生成 32 字节随机主密钥与 8 字节随机盐(
GetStrongRandBytes); - 用口令加密主密钥,
WriteMasterKey写入数据库; - 在
WalletBatch事务中逐个调用各ScriptPubKeyManager::Encrypt加密全部私钥——任何一步失败则TxnAbort并直接assert(false)终止,让进程重载未加密的钱包,避免"一半密钥已加密、一半未加密"的损坏状态; - 事务提交后调用
GetDatabase().Rewrite()全量重写钱包文件。源码注释解释了动机:"否则数据库文件的空闲空间(slack space)里可能残留未加密私钥的碎片"——这正是 0.4.0 发布说明中"先备份、后加密、确认后删备份"建议在实现层的呼应; - 最后自动
Unlock并重建钱包(生成新的 HD 种子/活跃描述符)。
4. 当前仓库中的操作入口
0.4.0 时代的操作入口是 GUI 的 "Options → Encrypt Wallet" 菜单项;当前仓库中的等价操作是 JSON-RPC 接口(GUI 同样通过 RPC 转发,参见 rpcconsole.cpp 中 encryptwallet 的命令补全):
$ bitcoin-cli encryptwallet "my pass phrase"
wallet encrypted; The keypool has been flushed and a new HD seed was generated.
You need to make a new backup with the backupwallet RPC.
encrypt.cpp 中 encryptwallet 的实现与帮助文本要点:
- 前置检查:钱包不含私钥(
WALLET_FLAG_DISABLE_PRIVATE_KEYS标志)时报错"nothing to encrypt";已加密钱包再调用则报RPC_WALLET_WRONG_ENC_STATE(对应 0.4 时代"重复加密"的防护); - 参数约束:口令至少 1 个字符,但"应当足够长";
- 成功后返回提示:keypool 已被清空、生成了新 HD 种子,必须用
backupwallet重新备份——这正是 0.4.0 文档"加密前先备份 wallet.dat"建议的现代版本化表述; - 配套接口:
walletpassphrase(临时解锁)、walletlock(encrypt.cpp L200-L218 实现,移除内存中的口令)、walletpassphrasechange(更换口令,替代 0.4 时代"重新加密"的需求)。
四、兼容性问题:为什么不能回退到 0.4 之前
0.4.0 发布说明的两条兼容性警告,在当前仓库中都能找到对应实现证据:
- 旧版本读不了加密钱包:加密后钱包数据库中的私钥条目由明文私钥变为密文格式,旧版客户端的解析代码没有对应分支,加载即失败(文档表述为"启动时崩溃")。
- BDB 4.7 → 4.8 单向性:0.4 起使用 bdb 4.8,其日志文件 4.7 无法读取,因此升级后无法回退。需要说明的是,当前仓库的默认钱包数据库已不再是 Berkeley DB(见 src/wallet/db.cpp 与 src/wallet/sqlite.cpp 双后端,新钱包默认 SQLite),但"加密后的钱包不可回退到不支持加密的老版本"这一约束本身至今不变。
五、测试验证:wallet_encryption.py
对加密功能的行为验证集中在 test/functional/wallet_encryption.py。该功能测试覆盖的核心断言与发布说明的安全语义一一对应:
- 加密前可以正常用钱包,
encryptwallet后钱包自动处于锁定状态,发送/签名类操作必须先walletpassphrase解锁; - 错误口令被拒绝、
walletlock后再次锁死、重复encryptwallet报错等路径均有覆盖; - 节点重启后加密状态持久化——即 0.4.0 说明中"加密后永远无法使用 0.4 之前的客户端"在现代测试体系中的等价断言。
此外,src/bench/wallet_encrypt.cpp 提供了钱包加密操作的基准测试,可用于观察密钥批量加密在不同硬件上的耗时(这与 DEFAULT_DERIVE_ITERATIONS 的历史注释形成对照)。
六、实践清单(由 0.4.0 发布说明整理)
综合原始发布说明与当前源码实现,执行钱包加密的标准流程为:
- 备份:当前版本用
backupwalletRPC 备份钱包文件(0.4 时代是手工拷贝wallet.dat,路径为 Linux~/.bitcoin/、macOS/Users/(用户名)/Application Support/Bitcoin/、Windows%APPDATA%/Bitcoin/); - 加密:
bitcoin-cli encryptwallet "足够长的口令"; - 重新备份:加密会生成新 HD 种子与新活跃描述符,旧备份中的私钥结构已失效,必须立即再次
backupwallet; - 日常使用:需要发送或签名时
walletpassphrase,用完walletlock; - 口令保管:写在纸上存放在安全处,不与任何其他口令复用,只在客户端内输入;
- 心理预期:加密不隐藏地址与交易历史;口令遗忘等于资产丢失,不可恢复。
结语
0.4.0 发布说明篇幅不长,却定下了 Bitcoin Core 钱包加密此后十余年不变的骨架:口令经 SHA-512 多轮迭代派生 AES-256-CBC 密钥加密主密钥,主密钥再加密私钥,IV 用公钥哈希,加密过程事务化并以全量重写杜绝明文残留。今天仓库中 crypter.h、wallet.cpp 与 encrypt.cpp 里的每一处常量、每一个断言,几乎都能在 2010 年的这份发布说明中找到出处;而 wallet_encryption.py 则保证了这套语义在每个版本上持续可验证。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00