一句话版本
私钥就是一个随机数。只要这个随机数不够随机(可选范围太小、可预测、或者被重复使用),攻击者就不用破解任何密码学,直接把所有可能的私钥枚举一遍即可。 从"脑钱包"到 Profanity 靓号工具,再到 Milk Sad 事件,一次次盗币背后都是同一个原因:用了 32 位种子、时间戳或人想出来的句子,而不是真正的密码学安全随机数。
全文分三层:
- 通俗理解:256 位的锁,只用了 32 位的钥匙
- 实际用处:脑钱包、Android 随机数漏洞、Profanity、Milk Sad、Trust Wallet
- 原理与工程要点:熵、CSPRNG、怎么测试随机数,以及签名随机数
第一层:通俗理解
1.1 256 位的锁,32 位的钥匙
以太坊私钥是一个 256 位的数,可能的取值约 个,比可观测宇宙里的原子还多,猜中是不可能的。
但如果生成私钥的程序是这样的:
- 取当前时间(或一个 32 位的数)作为"种子";
- 用一个普通的伪随机数生成器从种子算出 256 位私钥。
那么私钥看起来有 256 位,实际上只有 亿种可能。一台显卡几个小时就能全部试一遍。
私钥的安全性取决于它的熵(真正不可预测的位数),而不是它的长度。
1.2 三种常见错误
| 错误 | 例子 |
|---|---|
| 种子太短 | 用 32 位整数或时间戳给随机数生成器做种子 |
| 用错生成器 | 用游戏、模拟用的伪随机数(如梅森旋转),它的输出可以被推算 |
| 人来"想"随机数 | 用一句诗、一个口号的哈希当私钥(脑钱包) |
第二层:实际用处
| 事件 | 原因 | 后果 |
|---|---|---|
| 脑钱包(2010 年代) | 用一句话的 SHA-256 当私钥 | 研究者发现大量常用句子、名言、歌词对应的地址都被机器人监控,一有入账就被秒转走 |
| Android SecureRandom(2013) | 系统随机数生成器有缺陷,部分签名重复使用了随机数 | 多个比特币钱包 App 的私钥可以被算出,资金被盗( 重复为何致命,见《椭圆曲线》) |
| Profanity(2022) | 一个流行的以太坊"靓号地址"生成工具,只用 32 位种子初始化 | 安全研究者披露后数月内,做市商 Wintermute 的一个靓号地址被盗约 1.6 亿美元 |
| Milk Sad(2023) | 命令行工具 Libbitcoin Explorer 的 bx seed 用梅森旋转 + 32 位时间种子生成助记词 |
研究团队发现大量钱包可被枚举,已有资金被盗 |
| Trust Wallet 浏览器扩展(2022–2023) | WebAssembly 版本的随机数生成器熵不足 | 一批在特定时期生成的钱包可被枚举,造成损失 |
Profanity 事件特别值得一提:"靓号"本身就是一个危险信号。为了得到开头是 0x0000 的地址,工具需要飞快地生成海量私钥,开发者为了速度牺牲了随机性。
第三层:原理与工程要点
3.1 熵和 CSPRNG
- 熵:衡量不可预测程度。一个公平的硬币是 1 位熵;一个 32 位种子最多 32 位熵,后面无论怎么"搅拌"都不会增加。
- CSPRNG(密码学安全伪随机数生成器):在给足初始熵之后,输出和真随机不可区分,看到之前的输出也推不出之后的输出。
| 正确的来源 | 错误的来源 |
|---|---|
操作系统:/dev/urandom、getrandom()、BCryptGenRandom |
rand()、Math.random()、random.random() |
浏览器:crypto.getRandomValues() |
梅森旋转(MT19937) |
Python:secrets 模块 |
用时间戳、进程号做种子 |
| 硬件钱包的真随机数发生器 | 自己想的句子、自己掷的少量骰子 |
3.2 为什么梅森旋转不行
梅森旋转(MT19937)是很好的统计随机数生成器,用于模拟和游戏。但它是线性的(基于 上的线性递推,和LFSR与流密码是同一类):看到 624 个连续的 32 位输出,就能还原它的全部内部状态,预测之后的所有输出。即使种子足够长,也不能用于密码学。
3.3 怎么测试
随机数的问题几乎不可能靠看输出发现:一个只有 32 位熵的生成器,输出看起来照样很随机,统计检验也都能通过。所以测试的重点在代码审查:
- 找出所有生成私钥、助记词、nonce、会话 token 的地方;
- 确认每处都直接调用操作系统或语言标准库的 CSPRNG;
- 确认没有用时间、PID、计数器做种子,没有"为了可复现"而固定种子;
- 对 WebAssembly、嵌入式设备、虚拟机刚启动时等"熵可能不足"的环境格外小心。
3.4 签名时的随机数
不仅生成私钥需要随机数,ECDSA 每次签名也需要一个随机数 ,它重复或可预测就会泄露私钥。现代做法是 RFC 6979:用私钥和消息哈希确定性地生成 ,彻底不依赖随机数生成器的质量。Ed25519 和 BLS 签名天生是确定性的(见《Schnorr签名》、双线性配对与BLS签名)。
3.5 给用户的建议
- 只用知名钱包和硬件钱包生成助记词;
- 不使用任何"靓号生成器",除非它是开源、经过审计、并使用 CSPRNG 的;
- 老钱包如果是用来历不明的工具生成的,把资产转到新生成的钱包。
总结
| 层次 | 要点 |
|---|---|
| 直觉 | 私钥安全看熵不看长度;32 位种子 = 43 亿种可能 |
| 案例 | 脑钱包、Android、Profanity、Milk Sad、Trust Wallet |
| 原理 | 只能用 CSPRNG;梅森旋转是线性的,可被预测 |
| 测试 | 靠代码审查而不是统计检验;签名用确定性 |