在 Web2 里,「你」是数据库里的一行记录:邮箱、密码哈希、用户 ID。忘记密码可以重置,账号被盗可以找客服——因为「你」归根结底是平台说了算。
在 Web3 里,「你」什么记录都不是。你是一段 256 位的随机数,以及从它单向推导出来的一切。没有谁的数据库里存着你,没有任何客服能帮你「找回账号」——这也是上一篇说的「没有密码找回」的真正含义。这一篇把这条推导链完整拆开:助记词怎么变成私钥、私钥怎么变成地址、签名为什么等于本人——然后你会亲手用 cast 把每一步走一遍。
一条不可逆的流水线
先记住全貌,再谈细节。从「12 个英文单词」到「0x 开头的地址」,中间经过的每一站都是单向函数——正着算秒出,倒着推不可行:
四个标准各管一站,名字值得记住,因为你会在每个钱包的文档里反复见到它们:
BIP-39(助记词 → 种子)。钱包先生成 128 位真随机熵,附加 4 位校验和凑成 132 位,切成 12 组 11 位,每组去查一张 2048 个单词的固定词表——这就是你的 12 个助记词。校验和的存在意味着抄错一个词会在录入时立刻报错,而不是悄悄打开一个别人的空钱包。随后,助记词(可选地加一个口令)经过 PBKDF2-HMAC-SHA512 拉伸 2048 轮,产出 512 位种子。
BIP-32 / BIP-44(种子 → 整棵密钥树)。种子不是直接使用,而是作为一棵层级确定性(HD)密钥树的根。树的路径有行业惯例:m/44'/60'/0'/0/0,其中 60' 是以太坊的币种编号,最后一段是账户序号。这就是为什么一个助记词能派生出无限多个账户——你在钱包里点「新建账户」,只是序号加一,全部能从同一份备份恢复。
secp256k1(私钥 → 公钥)。以太坊和比特币用的是同一条椭圆曲线。私钥就是那个 256 位随机数(64 个十六进制字符);公钥由椭圆曲线乘法算出,正向轻松、反向等价于离散对数难题——以现有算力,暴力破解 128 位熵需要的时间比宇宙年龄还长。
Keccak-256(公钥 → 地址)。对公钥做 Keccak-256 哈希,取最后 20 个字节,加上 0x 前缀,就是 42 字符的地址。你平时看到的混合大小写地址(如 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045)是 EIP-55 校验和:大小写本身编码了校验信息,钱包能据此发现你复制错了一位。
整条链路上唯一需要保密的是私钥和助记词;公钥和地址天生就是给人看的。记住一句行话:Not your keys, not your coins——你对链上资产的全部所有权,就是这对密钥的控制权,没有别的。
签名:证明「是你」,而不交出钥匙
私钥的核心用途不是解密——是签名。这是新手最容易混淆的地方,值得单独拆开。
当你「签」一条消息时,实际发生的是:消息先被哈希,然后私钥对哈希做 ECDSA 运算,产出一段 65 字节的签名(三个分量 v, r, s)。任何人拿到「消息 + 签名」,都能用 ecrecover 反推出签名者的地址,和声称的地址一比对,就知道这条消息是不是你签的。全程不需要看到私钥——这就是「签名即本人」的密码学含义。
但不是所有签名弹窗都一样安全。以太坊历史上有三代签名方式,安全等级天差地别:
eth_sign(盲签,危险)——对任意 32 字节哈希签名,钱包只给你看一串看不懂的十六进制。你签的可能是「转账授权」而不自知。MetaMask 现在对它直接弹红色警告。见到就跑。personal_sign(EIP-191,可读)——消息前固定加上"\x19Ethereum Signed Message:\n"前缀再哈希,钱包展示的是明文文本。常见于「钱包登录」(Sign-In with Ethereum)。前缀的设计保证了签名永远不可能被当成一笔真实交易重放。- EIP-712(结构化签名,首选)——签的是带类型的结构体,钱包能把每个字段平铺给你看;更关键的是 domain separator 把签名绑定到特定的链 ID 和合约地址——你在 A 应用签的授权,攻击者拿到 B 应用、拿到另一条链上都是废纸。ERC-20 的
permit、Uniswap 的 Permit2、OpenSea 的订单都建立在它之上。
最后强调一遍上一篇的预防针:签名不等于发交易。签名本身不上链、不花 Gas、不产生任何记录——危险在于你签下的那份「授权」之后可能被任何人提交上链执行。所以铁律不变:看不懂的签名请求,一律拒绝。
动手:用 cast 把密钥流水线走一遍
光说不练是 Web3 学习的大忌。打开终端,用第一篇装好的 Foundry 亲手生成一套密钥(放心,这只是随机数,不碰任何真金白银):
# 生成一个全新的随机密钥对(私钥 + 地址)
cast wallet new
# 或者生成一套 BIP-39 助记词
cast wallet new-mnemonic
# 已知私钥,推导地址(验证「私钥 → 地址」是确定性计算)
cast wallet address --private-key 0x你的私钥
# 用私钥签一条消息(personal_sign 格式)
cast wallet sign "hello, chain" --private-key 0x你的私钥
# 任何人都可以验证:签名 + 消息 → 反推地址
cast wallet verify 0x你的地址 "hello, chain" 0x上一步的签名
跑完这四条命令,本篇所有概念你都亲手摸过一遍了:密钥生成、地址派生、签名、验证。
再进一步,把这个开发私钥存成加密 keystore(带口令的 JSON 文件),之后部署合约时就不用把明文私钥贴进命令行:
# 交互式导入,私钥和口令都不会留在 shell 历史里
cast wallet import dev-account --interactive
# 确认它在列表里
cast wallet list
keystore 文件存在 ~/.foundry/keystores/,是加密存储——即使文件被偷,没有口令也解不开。这是本系列往后所有「需要私钥的操作」的标准姿势。
三条密钥卫生军规
本篇是全系列安全浓度最高的一篇,规矩立在这里,后面不再重复:
- 开发钱包和真实资产物理隔离。 为本系列新建的钱包只领测试币。永远不在这台机器的开发环境里导入你真实钱包的助记词或私钥。
- 私钥不进代码、不进 Git。 测试脚本需要私钥时,用 keystore(
--account dev-account)或环境变量,并把.env加进.gitignore。GitHub 上有机器人 7×24 小时扫描新提交里的私钥格式,泄露后余额通常在几分钟内被清空——这不是传说,是自动化产业。 - 助记词只存在于纸上。 手写、抄写两遍、分开存放。不截图、不拍照、不进云笔记、不发任何「客服」。索求助记词的人,百分之百是骗子——没有例外。
练习,以及第 3 篇
- 跑一遍上面的
cast wallet new → sign → verify全流程。然后故意改一个字符的消息再verify,观察验证失败——亲手确认「签名绑定消息的每一个字节」。 - 用
cast wallet new-mnemonic生成一套助记词,再用--mnemonic加--mnemonic-index 1派生第二个账户地址,对比序号 0 的地址——亲眼看到「一套助记词,无限账户」。 - 把你的开发地址在 Sepolia 区块浏览器上查一下。现在它大概率是空的、没有任何交易——记住「地址存在」不需要任何注册,它只是密码学推导的必然结果;等你第 1 篇领的测试币到账后,刷新看看多出了哪些字段。
第 3 篇我们让密钥动起来:交易到底长什么样、nonce 为什么能防重放、Gas 费怎么算、一笔交易从签名到打包上链要闯过哪几关——以及 EOA 和合约账户这对双胞胎,为什么决定了整个 EVM 的编程模型。