Private Key Tools

WIF → 私钥 HEX(含校验和验证)

把 Wallet Import Format 字符串逐层拆开:Base58Check 载荷、版本字节、32 字节私钥主体、可选的压缩标志字节,以及 4 字节双 SHA256 校验和 —— 再为识别出的网络重新推导整套结果。主网/测试网、压缩/非压缩都接受。

只接受 Base58Check:51 或 52 个字符,取自字母表 1–9 A–Z a–z,其中不含 0、O、I、l。会先验证 4 字节校验和;写错一个字符会被直接报错拦下,而不是产出一把错误的私钥。
还没有输入

请在上方输入内容后点击「转换」。不知道填什么?点「填入示例」即可载入占位符里的演示数据。

技术原理详解

本页计算了什么

你粘贴一个 WIF —— Wallet Import Format 字符串,也就是 2011 年以来每个钱包的「导入 私钥」输入框所期待的那种东西。本页会逐字节把它拆开,验证保护它的校验和,还原出 32 字节标量,读出压缩 标志与网络字节,然后在这把还原出来的私钥上重跑整条推导链。

载荷 = 0x80 ‖ k(32 字节)‖ 可选的 0x01   →   WIF = Base58Check(载荷)
WIF 文本→ Base58 解码→ 载荷 + 4 字节校验和→ 验证 SHA256d→ 版本 ‖ 私钥 ‖ 标志→ 地址

WIF 一共有四种形态,本页全都接受:主网压缩(K…/L…)、主网非压缩 (5…)、测试网压缩(c…)、测试网非压缩(9…)。网络由版本字节 自动识别并如实报告,地址推导也跟着识别出的网络走,而不是一律假设主网。

1. WIF 是什么,为什么会被发明出来

2011 年,在两个比特币客户端之间搬运私钥的唯一办法就是手抄 64 个十六进制字符。作为一种传输格式, 它糟糕透顶,原因有三:

Wallet Import Format 把这三个问题一起解决了。它用 Base58Check 把私钥包起来:一种 58 进制字母表,刻意去掉了四个容易混淆的字符 0、O、I、 l(同时也去掉了 +、/、=),再加上 4 字节校验和, 使得几乎每一个错字都能在推导出任何东西之前就被拦住。

属性WIF裸十六进制私钥
字符数51 或 5264
字母表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)含义
0180版本/网络字节 —— 主网
1 – 32320824c314…07a171ca私钥本身
33101压缩标志(只有压缩形式才有)
34 – 3743a0a0417校验和 = 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 个字节追加到 待编码的字节串末尾。验证时重新算一遍并对比:

校验和 = SHA256(SHA256(载荷))[0 … 3]   (4 个字节 = 32 位)

由于校验和本身也在解码器看到的数据里,只要有一个字符写错,两边几乎必然不一致。一次随机错字仍然 对上的概率是 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非压缩主网5515HssbguBnjbaUVZCsH9YuzUG7T4vYQf51iiodi4izdVy6aVNekU
0x80压缩(+0x01)主网K / L52KwVYL77Lcs1ckM39NvGjrtfKhU4KKdR2iB9Wq2FSMN6rbGK5tVqG
0xEF非压缩测试网95191eWBRijNxfiSZ4VVd3Tnb2Dm7RdhaCGMfakiLRELNF1saLqhym
0xEF压缩(+0x01)测试网c52cMrXo27C3vhsunWQmL5sEDAPKhMiz5WinDHywShwrUkrr1RkCJ4i

它们各自的校验和(都算在对应载荷之上):主网非压缩 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. 安全与互操作须知

8. 常见错误

9. 速查表

项目值
载荷版本 ‖ 私钥 或 版本 ‖ 私钥 ‖ 0x01
载荷长度33 字节(非压缩)或 34 字节(压缩)
校验和SHA256(SHA256(载荷)) 的前 4 字节
解码后总长度37 或 38 字节
字符串长度51 或 52 个字符
主网版本字节0x80 → 非压缩 5…,压缩 K…/L…
测试网版本字节0xEF → 非压缩 9…,压缩 c…
压缩标志出现 0x01 = 压缩;不出现 = 非压缩
加密了吗没有。WIF 是明文。
可逆吗可以,且不需要任何密钥 —— 解码就够了