WIF → 私钥 HEX(含校验和验证)
把 Wallet Import Format 字符串逐层拆开:Base58Check 载荷、版本字节、32 字节私钥主体、可选的压缩标志字节,以及 4 字节双 SHA256 校验和 —— 再为识别出的网络重新推导整套结果。主网/测试网、压缩/非压缩都接受。
请在上方输入内容后点击「转换」。不知道填什么?点「填入示例」即可载入占位符里的演示数据。
本页计算了什么
你粘贴一个 WIF —— Wallet Import Format 字符串,也就是 2011 年以来每个钱包的「导入 私钥」输入框所期待的那种东西。本页会逐字节把它拆开,验证保护它的校验和,还原出 32 字节标量,读出压缩 标志与网络字节,然后在这把还原出来的私钥上重跑整条推导链。
WIF 一共有四种形态,本页全都接受:主网压缩(K…/L…)、主网非压缩
(5…)、测试网压缩(c…)、测试网非压缩(9…)。网络由版本字节
自动识别并如实报告,地址推导也跟着识别出的网络走,而不是一律假设主网。
1. WIF 是什么,为什么会被发明出来
2011 年,在两个比特币客户端之间搬运私钥的唯一办法就是手抄 64 个十六进制字符。作为一种传输格式, 它糟糕透顶,原因有三:
- 毫无冗余。 十六进制字符串里没有任何东西能说「这就是我想要的那一串」。改掉一个 字符,你会得到一个完全不同、却完全合法的 256 位数字,它控制着另一个(几乎肯定为空的)地址。这种 失败是静默的,只会在钱没了的时候才浮出水面。
- 字形容易混淆。 在大多数字体里,
0与O、1与l与I长得几乎一样 —— 而这正是人们从纸质钱包上抄写 私钥时会犯的错误。 - 没有元数据。 裸标量无处记录它属于哪条链,也无处记录它对应的公钥该用 33 字节 压缩形式还是 65 字节非压缩形式。而这两件事都会改变最终地址。
Wallet Import Format 把这三个问题一起解决了。它用 Base58Check 把私钥包起来:一种
58 进制字母表,刻意去掉了四个容易混淆的字符 0、O、I、
l(同时也去掉了 +、/、=),再加上 4 字节校验和,
使得几乎每一个错字都能在推导出任何东西之前就被拦住。
| 属性 | WIF | 裸十六进制私钥 |
|---|---|---|
| 字符数 | 51 或 52 | 64 |
| 字母表 | Base58:1–9 A–Z a–z 去掉 0 O I l | 十六进制:0–9 a–f |
| 错字检测 | 4 字节 SHA256d 校验和 —— 写错一个字符会直接报错 | 无 —— 写错一个字符就是另一把私钥 |
| 是否记录网络 | 是,版本字节 0x80/0xEF | 否 |
| 是否记录压缩标志 | 是,末尾的 0x01 | 否 |
| 适合写在纸上吗 | 就是为此设计的 | 并不适合 |
2. 精确的字节布局
WIF 不过是一小段字节的 Base58Check 编码。在主网压缩的情形下,载荷是
0x80 ‖ 私钥 ‖ 0x01,共 34 字节;Base58Check 再追加 4 字节校验和,于是总共 38 字节,
编码成 52 个 Base58 字符。下面是演示 WIF 逐字节拆解的真实结果:
| 偏移 | 字节数 | 取值(演示 WIF) | 含义 |
|---|---|---|---|
| 0 | 1 | 80 | 版本/网络字节 —— 主网 |
| 1 – 32 | 32 | 0824c314…07a171ca | 私钥本身 |
| 33 | 1 | 01 | 压缩标志(只有压缩形式才有) |
| 34 – 37 | 4 | 3a0a0417 | 校验和 = SHA256(SHA256(载荷)) 的前 4 字节 |
其中有两行需要重点强调。
版本字节是网络元数据,不属于秘密本身。 它告诉钱包这把私钥属于哪条链,好让推导出的 地址使用正确的前缀。把它改掉,你得到的就是另一条链上合法有效的 WIF —— 同样的 32 字节,不同的地址。
末尾的 0x01 不是私钥的一部分。 它是一个单字节标志,回答一个 32 字节
标量自己回答不了的问题:这把私钥的公钥在序列化时,应该用 33 字节的压缩形式,还是 65 字节的非压缩形式?
这个标志不是校验和、不是计数器、也不是补位。它只有一个合法取值 —— 出现 0x01 表示「压缩」,
不出现表示「非压缩」。33 字节的载荷若以其它任何字节结尾,都属于格式错误,会被直接拒绝。在代码里,
识别私钥就是一次长度判断加一次单字节判断:
rest = payload[1:] # 剥掉版本字节
if len(rest) == 33 and rest[-1] == 0x01:
compressed = True; key = rest[:32]
elif len(rest) == 32:
compressed = False; key = rest
else:
reject("载荷既不是 33 字节也不是 34 字节")
3. 校验和,以及它如何抓住一个错字
Base58Check 把校验和算在载荷之上 —— 版本字节、私钥和标志 —— 再把它的前 4 个字节追加到 待编码的字节串末尾。验证时重新算一遍并对比:
由于校验和本身也在解码器看到的数据里,只要有一个字符写错,两边几乎必然不一致。一次随机错字仍然 对上的概率是 1 / 232,大约是四十三亿分之一。下面是演示 WIF 以及真实「差一点」的样子 —— 这些全是实算结果,不是示意图:
| 输入 | 解码器看到的载荷 | 字符串里的校验和 | 应有的校验和 | 结果 |
|---|---|---|---|---|
…GK5tVqG(正确) |
800824c314…171ca01 |
3a0a0417 |
3a0a0417 |
合法 |
…GK5tVqH —— 末字符 G→H |
800824c314…171ca01(完全没变!) |
3a0a0418 |
3a0a0417 |
拒绝 |
…GK5tVqF —— 末字符 G→F |
800824c314…171ca01 |
3a0a0416 |
3a0a0417 |
拒绝 |
| 把中间两个字符对调 | 800824c314772b53…dd97265df501 |
3a0a0417 |
一个完全不同的值 | 拒绝 |
| 删掉最后一个字符(51 字符) | 02351b1dd8a0f380…cacf94704e84 |
6f5872d4 |
一个完全不同的值 | 拒绝 |
在中间敲进一个 O |
根本无法解码 | — | 拒绝 | |
第一组「差一点」最有教育意义。改动 Base58 字符串的最后一个字符,只会扰动解码数据的最后一个
字节,于是载荷逐字节完全相同,只有校验和变了:3a0a0417 变成 3a0a0418,
差一个比特。你本来会得到的那把私钥依然是正确的私钥,但这个字符串仍然被拒绝。把校验和放在末尾,意义就在
这里。
O 那一行展示了第二层防线:Base58 里根本没有 O,所以字符串连解码这一步都
过不去。抄错的纸质钱包,在轮到校验和之前就会被字母表本身抓出来。
4. 前缀:哪条网络,哪种形式
字符串的首字符最终反映的就是版本字节。下面是演示私钥的四种变体,每一种都附上实算出的校验和:
| 版本字节 | 形式 | 网络 | 首字符 | 长度 | 演示私钥 |
|---|---|---|---|---|---|
0x80 | 非压缩 | 主网 | 5 | 51 | 5HssbguBnjbaUVZCsH9YuzUG7T4vYQf51iiodi4izdVy6aVNekU |
0x80 | 压缩(+0x01) | 主网 | K / L | 52 | KwVYL77Lcs1ckM39NvGjrtfKhU4KKdR2iB9Wq2FSMN6rbGK5tVqG |
0xEF | 非压缩 | 测试网 | 9 | 51 | 91eWBRijNxfiSZ4VVd3Tnb2Dm7RdhaCGMfakiLRELNF1saLqhym |
0xEF | 压缩(+0x01) | 测试网 | c | 52 | cMrXo27C3vhsunWQmL5sEDAPKhMiz5WinDHywShwrUkrr1RkCJ4i |
它们各自的校验和(都算在对应载荷之上):主网非压缩 b6f3d7f5、主网压缩
3a0a0417、测试网非压缩 7271604c、测试网压缩 892c3e23。
K 或 L,而不是固定的一个字母?
Base58 字符串的首字符代表整个大数的最高位,而主网压缩载荷恰好压在一条边界上:载荷的最高字节
永远是 0x80,但整个 34 字节大数的数值随私钥而变,所以最高位 Base58 数字可能落在
K,也可能落在 L。这里没有任何需要解读的东西 —— 两者含义完全一样。真正携带
信息的只有版本字节。
5. 压缩标志在 WIF 里,不在私钥里
这是本页最重要的一个事实。32 字节的标量 k 不知道自己、也不可能知道自己该配压缩公钥还是
非压缩公钥。这个选择存在于 WIF 的末尾字节里,而它会改变地址 —— 因为地址是序列化后的公钥的哈希,
而两种序列化是两串不同的字节。
压缩 WIF —— K…
KwVYL77Lcs1ckM39NvGjrtfKhU4KKdR2iB9Wq2FSMN6rbGK5tVqG
公钥 02ff812e…67ee80(33 字节)
hash160 21f57b6debcfc5b67182dfb4af1641f0a8189012
规范地址 146ZNjH7XTXnMd1ofe3z7CL2i8WM7N2UqT
非压缩 WIF —— 5…
5HssbguBnjbaUVZCsH9YuzUG7T4vYQf51iiodi4izdVy6aVNekU
公钥 04ff812e…b424ae(65 字节)
hash160 3f0e966dc089c611a9c1d03bd4f69ea53b0223f9
规范地址 16kR3eiswUY6tmGnxZhwQmW6LAvTLGWG2G
同一个标量,两串字符,两个地址。两者都能用同一把私钥花掉;只有一个同时认识两种形式的钱包才能把两个
都找出来。另外请注意,SegWit 与 Taproot 地址类型(bc1q…、bc1p…)永远由
压缩公钥构建,因为 witness 程序只在压缩公钥上有定义 —— 非压缩形式是一条只产出 P2PKH 地址的
历史遗留路径。
如果你导入的是压缩 WIF,而币是收到非压缩地址上的,钱包就会去查错误的地址、显示空余额。对于 2012 年之前导出的私钥,反过来的情况同样常见。请不要据此断定资金已经丢失:把两种形式都推导一遍 —— 下面的 结果面板正是在做这件事 —— 看看币究竟在哪个地址上。同样要注意,你无法通过「改掉标志位再重算校验和」 来修复它:那样得到的是一个指向另一个地址的合法 WIF,行为上完全正确,但并不能找回原来的币。
6. 实例演算
对示例 WIF KwVYL77Lcs1ckM39NvGjrtfKhU4KKdR2iB9Wq2FSMN6rbGK5tVqG:
| 步骤 | 值 |
|---|---|
| 长度 | 52 个 Base58 字符,解码后 38 字节 |
| 解码载荷(34 字节) | 800824c314772b2c2859c889471390a36bf66e53a1ca1642f96f0f2c9307a171ca01 |
| 版本字节 | 0x80 → 比特币主网 |
| 私钥主体(32 字节) | 0824c314772b2c2859c889471390a36bf66e53a1ca1642f96f0f2c9307a171ca |
| 压缩后缀 | 0x01 → 压缩 |
| SHA256(SHA256(载荷)) | 3a0a0417ffa98881ccdd0c7b283639584e0e909389f6bfbbdbac54547d2b732d |
| 字符串携带的校验和 | 3a0a0417 —— 与上面的前 4 个字节一致 |
| 还原出的标量 | 0824c314…07a171ca,落在 [1, n−1] 区间内 |
| 压缩公钥 | 02ff812e26116a9aa140abe629e7f8a38401653caf925119def59ed3758c67ee80 |
| 规范 P2PKH 地址 | 146ZNjH7XTXnMd1ofe3z7CL2i8WM7N2UqT |
| P2SH-P2WPKH 地址 | 3DZT76KyKyc5zkzvLugP8umgt4RPY4XPQw |
| P2WPKH 地址 | bc1qy86hkm0telzmvuvzm76279jp7z5p3yqjnaerk6 |
| P2TR 地址 | bc1pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3sh54u63 |
把还原出的私钥换到另一条网络上重新编码,得到
cMrXo27C3vhsunWQmL5sEDAPKhMiz5WinDHywShwrUkrr1RkCJ4i,它对应的地址是
micWfnN6LUy38jVRPD2Mw7YMa873zjixGw(压缩)和
mmGNLhorkVyMfskQg8gKEgiRCAXAGnxeBv(非压缩)。同一个秘密,靠两个字节的元数据就能变出四个
不同的「第一个地址」。
7. 安全与互操作须知
- WIF 就是私钥。 它是纯粹的明文编码,没有任何加密。任何看到这串字符的人 —— 截图、
聊天记录、剪贴板管理器、备份文件 —— 都能花掉这些币。加密导出确实存在,但那是另一种格式(BIP38 的
密钥以
6P开头);WIF 从来不是加密的。 - 校验和不是身份认证。 它能发现无心的错字,但不能证明这串字符是谁造的。任何人都
可以改掉版本字节并重新算出一个完全有效的校验和 —— 本页把这样的字符串(
0x81)显示为 「未知前缀」,而不是假装它属于某条真实网络。 - 网络混淆是静默的。 测试网与主网使用不同的版本字节和不同的地址前缀。把测试网 WIF 导入主网软件,会推导出主网地址,而这把私钥确实是真的 —— 只是你在那里找不到测试网的币。
- 能避免就绝不手抄 WIF。 校验和能抓住写错一个字符,却抓不住「从错误的地方复制了 一串看起来正确的字符串」。真正要紧的时候,请把还原出的十六进制与来源对比一遍。
8. 常见错误
- 把 WIF 和十六进制当成同一串东西。 WIF 是 51 或 52 个 Base58 字符,十六进制是 64 个。字母表不一样,长度也不一样。
- 以为
K…开头的私钥「更新」或「更好」。 那个字母是版本字节与私钥 数值共同作用的结果;信息在标志字节里,不在首字母里。 - 手工修改压缩标志。 加上或去掉
0x01、重算校验和,你得到的是一个指向 另一个地址的合法 WIF —— 那不是把第一个地址「转换」过来了。 - 去掉
0x01却指望地址不变。 地址一定会变。这正是第 5 节的全部内容。 - 忘了版本字节也在校验和的输入里。 改网络字节就必须重算校验和,这正是 WIF 不能 手改的原因。
- 去找补位符。 Base58Check 里没有
=,也没有固定块大小。所谓 WIF 里 出现=,就说明它根本不是 WIF。
9. 速查表
| 项目 | 值 |
|---|---|
| 载荷 | 版本 ‖ 私钥 或 版本 ‖ 私钥 ‖ 0x01 |
| 载荷长度 | 33 字节(非压缩)或 34 字节(压缩) |
| 校验和 | SHA256(SHA256(载荷)) 的前 4 字节 |
| 解码后总长度 | 37 或 38 字节 |
| 字符串长度 | 51 或 52 个字符 |
| 主网版本字节 | 0x80 → 非压缩 5…,压缩 K…/L… |
| 测试网版本字节 | 0xEF → 非压缩 9…,压缩 c… |
| 压缩标志 | 出现 0x01 = 压缩;不出现 = 非压缩 |
| 加密了吗 | 没有。WIF 是明文。 |
| 可逆吗 | 可以,且不需要任何密钥 —— 解码就够了 |