压缩公钥 → 曲线点、hash160、地址
把 33 字节公钥解压回完整的曲线点:从 X 还原 Y、验证它确实落在 secp256k1 上,再做哈希并编码成 P2PKH、P2SH-P2WPKH、P2WPKH 与 P2TR 地址。同一个点的非压缩形式会并排显示,因为它会产出另一个地址 —— 而整个过程完全不使用私钥。
请在上方输入内容后点击「转换」。不知道填什么?点「填入示例」即可载入占位符里的演示数据。
本页计算了什么
你给它一个压缩公钥 —— 66 位十六进制、以 02 或 03 开头 ——
它会做钱包每次加载地址时都要做的两件事:把公钥解压回完整的曲线点,验证这个点确实落在
secp256k1 上,然后把它的哈希编码成它能生成的所有地址类型。整个过程与本页任何地方都不涉及私钥,也根本
不需要私钥。
本页还会显示同一个点的非压缩序列化形式,以及由它得到的另一个地址,因为 压缩/非压缩的区别正是比特币里「钱包显示余额不对」这类困惑最常见的来源。
1. 02/03 前缀就是一个奇偶位
公钥是素域 p 上曲线 y² = x³ + 7 的一个点 (X, Y)。老实存储它要
花 64 字节。2012 年整个生态改成了只存 X 加一个比特,因为 Y 可以被精确地重算
出来。那个比特被塞进了序列化的第一个字节里:
| 前缀字节 | 总长度 | 含义 |
|---|---|---|
0x02 | 33 字节(66 位 hex) | 压缩编码;真实 Y 是偶数 |
0x03 | 33 字节(66 位 hex) | 压缩编码;真实 Y 是奇数 |
0x04 | 65 字节(130 位 hex) | 非压缩:04 ‖ X ‖ Y,两个坐标都显式存储 |
0x06/0x07 | 65 字节 | 混合形式(历史遗留):X ‖ Y 外加一个必须与 Y 一致的冗余奇偶位 |
这个比特不是校验和,也不是版本号。它只回答一个问题:「这个 X 对应的两个可能点里,我要的是哪一个?」
2. 解压缩究竟怎么算
给定 X,曲线方程就变成一个单未知数的方程:
域上的二次方程最多两个解;若其中一个是 Y,另一个必然是 p − Y,因为在模
p 意义下 (p − Y)² = Y²。这两个根的奇偶性必然相反:p
是奇数,所以 p − Y ≡ −Y (mod 2) 会翻转最低位。一个偶根、一个奇根 —— 这就是一个比特
够用的原因。
还要注意,随机 X 大约一半的时候根本没有平方根:域元素里只有约一半是二次剩余,所以以
02 或 03 开头的 33 字节字符串里,约有一半压根不是合法的公钥。
求根通常用试探法(Tonelli–Shanks),但 secp256k1 的素数形状很特殊,存在闭式解。因为
对任何二次剩余 a 成立。理由很短:若 y = a(p+1)/4,则
y² = a(p+1)/2 = a · a(p−1)/2,而欧拉判别法告诉我们:只要
a 是二次剩余,就有 a(p−1)/2 ≡ 1。于是可以直接做一次以固定指数
为幂的模幂运算
然后如果结果的奇偶性不对,就把它替换成 p − Y。在 secp256k1 上
p mod 4 = 3 精确成立,所以这条捷径永远可用;而 p ≡ 1 (mod 4) 的曲线就得用
更慢的通用算法。
很容易误以为 33 字节形式「丢掉」了什么。并没有:这个编码与「该 X 下的全部合法公钥」之间是双射。
这里也没有从私钥里恢复出任何东西 —— 信息从来不曾被移除,只是由曲线方程重新算了出来。
02 ‖ X 与非压缩的 04 ‖ X ‖ Y 描述的是同一个点。
3. 为什么必须拒绝曲线外的点
曲线方程是一道检验,不是一条提示:(X, Y) 要么满足 Y² = X³ + 7 (mod p),
要么不满足,不存在「差不多在曲线上」的状态。校验一旦失败,你手里拿的就不是公钥 ——
它不是群里的元素,用它做标量乘没有定义,其上任何协议都只是在吞垃圾。
这件事之所以要紧,是因为存在一整个被称为无效曲线攻击(invalid curve attack)的 攻击家族。假设某个协议会拿对方提供的点去乘一个秘密标量(ECDH 就是这么做的)。如果这个点没有被验证, 攻击者就可以发来落在另一条曲线上的点,或者落在一个小阶子群里的点。乘法的结果于是只依赖秘密的 少数几个比特。攻击者用许多这样的点做试验,再用中国剩余定理把零碎的答案拼起来,就能还原出完整的私钥。 已经有好几个 TLS 实现和智能卡实现正是这样被攻破的;而防御只有一行代码 —— 使用任何外部提供的点之前先验证 它。
需要诚实地补充一句:secp256k1 的余因子是 1、阶是素数 n。它没有小阶
子群,二次扭的阶也不光滑,所以在这条曲线上,教科书版本的小子群攻击无从下手。仅仅因为有人解析
过一个坏点,比特币并不会因此丢失。但这份纪律仍然不是可选项,有三个务实理由:
- 校验的代价微不足道 —— 一次立方、一次加法、一次平方,没有任何性能理由跳过它。
- 安全性证明依赖它。 方案的安全性都是在合法群元素上证明的;接受非法元素 会让证明失效,而具体的破解方式往往由后来的人发现。
- 真实软件不只服务一条曲线。 一个接受曲线外点的库,离「被用在一条真会出事的曲线上」 只有一次重构之遥,而这个 bug 不会自己示意。
因此本页会解析点、校验方程,失败就拒绝继续。用 02 后跟 64 个 0 作为测试输入非常合适:
它会以一条可读的错误消息被拒绝,而不是默默从一个毫无意义的数字推导出一堆地址。
规范的 SEC1 编码要求 0 < X < p。严格的解码器 —— 包括比特币共识所依赖的
libsecp256k1 —— 会把大于等于 p 的 X 当作非规范编码而拒绝。本教学实现则
把 X 对 p 取模后继续,所以这里会有少数数值上古怪的输入被接受,而共识解析器
会拒绝它们。这是刻意的简化,也正是逐字节比较两个实现时会出现的那种差异。
4. 推导地址不需要私钥 —— 以及它的后果
从这里往下全是公开算术:把序列化公钥做哈希,再把哈希编码。这条流水线里没有任何一步需要标量
k,也没有任何一步可以反推出 k。
- 观察钱包(watch-only wallet)。 把一个公钥或扩展公钥(xpub)交给钱包,它就能 永远生成并监视地址,而从不持有花费私钥;种子留在硬件设备里即可。
- 任何知道你公钥的人,就掌握了你的全部地址。 每一笔你花掉的 P2PKH 输入,
其
scriptSig里都直接嵌着那 33 字节公钥。仅凭一笔交易,旁观者就能推导出本页全部四类地址 并永远盯着它们。公开公钥对资金不危险 —— 离散对数保护着它们 —— 但对隐私是致命的。 - 链上分析就是这么做的。 地址聚类、「这两个地址属于同一实体」这类判断,完全建立在 这种廉价、公开的推导之上。这就是为什么 HD 钱包(BIP32)为每个地址用一把新密钥,也是为什么 Taproot 把真实密钥藏在一个 tweak 之后。
- 这也是量子风险不对称的原因。 已经暴露在链上的公钥,会直接面对 Shor 算法;而一个 从未暴露过密钥、也从未花费过的基于哈希的地址则不会 —— 直到它花费的那一刻。
5. 压缩与非压缩
两种形式都是同一个点的合法编码,也都为共识所接受,但它们仍然会产出不同的地址 —— 因为地址 是序列化字节的哈希,而两者的序列化字节不同。
| 压缩 | 非压缩 | |
|---|---|---|
| 序列化 | 02/03 ‖ X | 04 ‖ X ‖ Y |
| 长度 | 33 字节 | 65 字节 |
| 演示私钥的 hash160 | 21f57b6debcfc5b67182dfb4af1641f0a8189012 | 3f0e966dc089c611a9c1d03bd4f69ea53b0223f9 |
| 演示私钥的 P2PKH 地址 | 146ZNjH7XTXnMd1ofe3z7CL2i8WM7N2UqT | 16kR3eiswUY6tmGnxZhwQmW6LAvTLGWG2G |
| 花费时的 scriptSig | 约 107 字节(压栈 + 约 72 字节签名 + 33 字节公钥) | 约 139 字节(同样结构,公钥为 65 字节) |
| 能否用于 P2WPKH/P2SH-P2WPKH/P2TR | 可以 —— 而且这是唯一允许的形式 | 不能;witness 程序只定义在压缩公钥上 |
| 今日地位 | 所有地方的默认选择(2012 年起) | 历史遗留,但依然有效、可花 |
由于非压缩公钥多出 32 字节、又直接嵌入输入脚本,从非压缩地址花费的手续费更高。这就是压缩形式最终 胜出的全部原因。
6. 实例演算 —— 真实的演示公钥
下面每一个值都是本页对输入框里的示例(02ff812e…67ee80)实际算出的结果:
| 步骤 | 值 |
|---|---|
| 输入,66 位十六进制 | 02ff812e26116a9aa140abe629e7f8a38401653caf925119def59ed3758c67ee80 |
| 前缀字节 | 02 → Y 是偶数 |
| X(前缀之后的 64 位) | ff812e26116a9aa140abe629e7f8a38401653caf925119def59ed3758c67ee80 |
| X³ + 7 mod p(要开平方的那个值) | 174ca53008928cd3c9c380e12c4b1dee0c5a0b6d851b1c472a58d744ca28c6dc |
| 还原出的 Y —— 偶根 | 8f49cde13c7ffd64f1d730bf87a743a578eda4971ddac4a08118160732b424ae |
| Y 的最后一位十六进制 | e → 偶数,与 02 前缀一致 |
| 另一个根 p − Y | 70b6321ec380029b0e28cf407858bc5a87125b68e2253b5f7ee7e9f7cd4bd781 |
| p − Y 的最后一位十六进制 | 1 → 奇数,与孪生根的预期相符 |
| 曲线方程校验 | 通过 —— Y² 等于 X³ + 7 mod p |
| hash160(压缩) | 21f57b6debcfc5b67182dfb4af1641f0a8189012 |
| P2PKH(压缩) | 146ZNjH7XTXnMd1ofe3z7CL2i8WM7N2UqT |
| P2SH-P2WPKH | 3DZT76KyKyc5zkzvLugP8umgt4RPY4XPQw |
| P2WPKH | bc1qy86hkm0telzmvuvzm76279jp7z5p3yqjnaerk6 |
| P2TR | bc1pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3sh54u63 |
同一个点的非压缩对应物是那个 130 字符的字符串 04ff812e…b424ae,它产出的是完全不同的
地址 16kR3eiswUY6tmGnxZhwQmW6LAvTLGWG2G。
最后把前缀比特翻过来:同样的 X,声明为 03。本页于是还原出奇根
70b6321e…cd4bd781,即原点的负元。它的 hash160 是
bf2aa927b06822b645d48e54ac82676c2e586675,P2PKH 地址是
1JRoAmA3S9nPbucndLyARsmY2Awuzgdazb。如果 P = k·G,这个孪生点就是
(n − k)·G —— 编码里改一个比特,就是另一把私钥。
7. 链条上接下来发生什么
- P2PKH(
1…)——Base58Check(0x00 ‖ hash160)。见 hash160 → 地址。 - P2SH-P2WPKH(
3…)——Base58Check(0x05 ‖ hash160(0x0014 ‖ hash160))。 对演示公钥来说,内层脚本哈希是823333d5fd23a83661e8e47629552c4a5bfc8b6e。 - P2WPKH(
bc1q…)——bech32("bc", 0, hash160),witness 程序 就是 hash160 本身,没有任何改动。 - P2TR(
bc1p…)—— 完全不同的构造:x 坐标先经TapTweak调整,输出公钥为Q = P + t·G。对演示公钥来说,tweak 是bae9168523281e7f9e91d897a8d23f7ae6fe66d61854019dcb8abb5185e8fa4d,输出公钥是dc07734d9b91d7231b8574b459ad8e65d8351c1c4930ad2e171447297bde59a3。见 bech32 / bech32m 解码。
8. 安全须知
- 公钥本来就是用来公开的 —— 在离散对数问题依然困难的前提下,它不会泄漏任何关于私钥 的可用信息。但它并不私密:这里列出的每一类地址,任何人都能永远从它推导出来。
- 收到点一定要验证。 曲线外的点不是公钥。请像本页一样,在任何算术碰到它之前就在边界 上拒绝掉。
- 量子计算是长期的例外。 Shor 算法直接解离散对数,所以已经暴露在链上的公钥才是受 威胁的一方,而不是从未花费过的哈希地址。
9. 常见错误
- 把压缩公钥和非压缩公钥当成两把不同的密钥。 它们是同一个点、同一把私钥的两种序列化 形式,只有地址不同。
- 混淆两个 hash160。
21f57b6d…与3f0e966d…都叫「演示 私钥的 hash160」—— 分别是它压缩形式与非压缩形式的哈希。说的时候一定要讲清是哪一个。 - 以为
02透露了关于私钥的信息。 它是Y的奇偶性,是点的 属性,不是k的属性。 - 以为以
02/03开头的 33 字节串一定合法。 其中只有约一半 能让X³ + 7开出平方根,其余会被拒绝。 - 把公钥当成地址。 那 66 个字符从不是地址;地址是公钥的 20 字节哈希经过编码的结果。
- 忘了奇偶位会改变密钥。
02 ‖ X与03 ‖ X是互为负元的 两个不同点,对应不同的私钥(k与n − k)和不同的地址。
10. 速查表
| 项目 | 值 |
|---|---|
| 输入 | 66 位十六进制:02 或 03 后跟 32 字节的 X |
| 前缀含义 | 02 = Y 为偶,03 = Y 为奇 |
| 域素数 p | 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F |
| p mod 4 | 3 —— 因此可以使用闭式平方根 |
| 开方指数 (p+1)/4 | 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFF0C |
| 两个根 | Y 与 p − Y,奇偶性必然相反 |
| 曲线方程校验 | Y² ≡ X³ + 7 (mod p) |
| 输出 | X、Y、奇偶性、hash160、P2PKH(压缩与非压缩)、P2SH-P2WPKH、P2WPKH、P2TR |
| 需要私钥吗 | 不需要 —— 以上没有一步能反推 k |