私钥 → 压缩公钥
在 secp256k1 曲线上把生成元 G 乘以私钥标量 k,再把得到的点序列化成以 02 或 03 开头的 33 字节形式。这一步是「秘密数字」变成「可以公开的东西」的分界线,比特币里所有其它地址类型都建立在它之上。
请在上方输入内容后点击「转换」。不知道填什么?点「填入示例」即可载入占位符里的演示数据。
本页计算了什么
你给它一个 256 位的数字 —— 私钥 k —— 它算出压缩公钥。
在服务器内部,这就是一次运算:
1. 私钥到底是什么
比特币私钥不是文件、不是密码、也不是一坨随机字节。它是一个整数 —— 用椭圆曲线密码学的 术语说,是一个标量 —— 并且必须满足:
两个边界值被排除在外,这在实践中非常关键:
k = 0会得到0 · G,也就是所谓的无穷远点。它没有坐标,无法 序列化成公钥。k ≥ n会绕回来。因为曲线的点群是阶为n的循环群:n · G = ∞, 于是(n+1) · G = G。一个等于n+1的私钥看起来是合法的 32 字节数字, 行为上却悄悄等同于1。
所以可用私钥总数是 n − 1 ≈ 1.158 × 1077 个。有人算过:就算用每秒一万亿个的
速度去撞,也需要大约 1058 年。
| 属性 | 取值 |
|---|---|
| 长度 | 256 位 / 32 字节 / 64 位十六进制 |
| 合法范围 | 1 … n−1 |
| 盲猜命中概率 | 约 1 / 2256(约 1.16 × 1077 个可能值) |
| 它必须是什么 | 均匀随机的,或由均匀随机的熵推导而来 |
| 它绝不能是什么 | 人为选择的一句话的哈希、计数器、时间戳、书里的句子 |
历史上每一桩与「机器被入侵」无关的大额比特币失窃,根源都是私钥可预测:用时钟做种子的弱随机数
发生器、脑钱包口令、用手机序列号造出来的密钥。这里的数学是破不开的,出问题的永远是它周围的熵源。
请使用 random_bytes(32)(本服务器就是这么做的)或硬件钱包的安全芯片。
2. 曲线:为什么公钥是一个「点」
比特币使用的曲线叫 secp256k1,定义在模素数 p 的整数域上:
任何满足这个方程的 (x, y) 都是曲线上的一个点。由于方程是模 p 的,这条「曲线」并不是
一条光滑的弧线,而是散布在 256 位宽有限网格上的一堆离散点 —— 但它依然构成一个数学上的群,
正是这一点让密码学得以建立其上。
点可以相加(几何上:过两点作直线,找到第三个交点,再沿 x 轴翻转),因此也可以把一个点 反复与自身相加,实现「整数乘法」。这套群运算就是比特币密钥推导的全部引擎。
| 常量 | 取值 |
|---|---|
| 域素数 p | 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F |
| 曲线阶 n | 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 |
| 生成元 Gx | 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 |
| 生成元 Gy | 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 |
| 余因子 h | 1(群大小相同,不需要防范小子群攻击) |
注意 p 和 n 是两个不同的数,新手常把它们搞混。
p 约束的是坐标的取值范围,n 约束的是私钥的取值范围。
3. P = k·G 到底怎么算
没有闭式捷径。标准做法是倍点-加点(double-and-add),从高位到低位遍历 k 的 256 个比特:
R = 无穷远点
for bit in bits_of(k): # 256 次循环
R = double(R) # R = R + R
if bit == 1:
R = add(R, G) # R = R + G
return R
大约是 256 次倍点和 128 次加点,几百次有限域运算 —— 所以现代笔记本每秒能算几万次,而本项目的 纯 PHP + GMP 实现大约每秒 80 次。
从 k 算到 P 只要 256 步。但从 P 反推 k —— 这就是
椭圆曲线离散对数问题 —— 目前没有任何高效算法:最通用的方法(Pollard rho)需要大约
√n ≈ 2128 次运算量级。正是这种不对称性,让公钥可以被安全地公开。
上面的朴素循环会根据私钥比特走不同分支,于是运行时间和功耗都会泄漏信息。生产级实现会使用 常数时间标量乘与随机盲化。本页面是教学工具;真要管钱请用 libsecp256k1 或硬件钱包。
4. 点的序列化:33 字节还是 65 字节
一个点有两个 256 位坐标,所以最直白的编码是 64 字节再加上一个标志位。SEC1 标准定义了几种方式:
| 形式 | 编码 | 长度 | 首字节 |
|---|---|---|---|
| 非压缩 | 04 ‖ X ‖ Y | 65 字节(130 位 hex) | 04 |
| 压缩(Y 为偶) | 02 ‖ X | 33 字节(66 位 hex) | 02 |
| 压缩(Y 为奇) | 03 ‖ X | 33 字节(66 位 hex) | 03 |
| 混合(历史遗留) | 06/07 ‖ X ‖ Y | 65 字节 | 06/07 |
| x-only(BIP340 / Taproot) | X | 32 字节(64 位 hex) | 无 |
5. 奇偶性技巧:为什么 32 字节就够了
压缩并不是「有损的取巧」,它是精确无损的 —— 因为 Y 可以从 X 完全恢复出来。
给定 X,曲线方程给出
它恰好有两个解:Y 和 p − Y。这两个根奇偶性必然相反:一个为偶数,
另一个为奇数(因为 p 是奇数,p − Y ≡ −Y 会翻转最低位)。所以只要 1 个比特 ——
「Y 是奇数还是偶数?」—— 就足以在两者之间做出选择。这个比特正是 02/03 前缀
所保存的信息。
因此解压缩是一次真刀真枪的计算:把 X 的三次方加 7,再做模平方根(由于 p ≡ 3 (mod 4),
有闭式解 Y = (X³+7)(p+1)/4 mod p),最后若奇偶性与请求不符则取负。
6. 实例演算
下面就是本页对示例私钥 0824c314…171ca 实际算出的值:
| 步骤 | 值 |
|---|---|
| k | 0824c314772b2c2859c889471390a36bf66e53a1ca1642f96f0f2c9307a171ca |
| P = k·G 的 X | ff812e26116a9aa140abe629e7f8a38401653caf925119def59ed3758c67ee80 |
| P 的 Y | 8f49cde13c7ffd64f1d730bf87a743a578eda4971ddac4a08118160732b424ae |
| Y 的奇偶性 | 末位十六进制是 e → 偶数,故前缀为 02(若为奇数则前缀是 03) |
| 压缩公钥 | 02ff812e26116a9aa140abe629e7f8a38401653caf925119def59ed3758c67ee80 |
| 非压缩公钥 | 04ff812e…8f49cd…b424ae(65 字节) |
| hash160(压缩) | 21f57b6debcfc5b67182dfb4af1641f0a8189012 |
| hash160(非压缩) | 3f0e966dc089c611a9c1d03bd4f69ea53b0223f9 |
| P2PKH 地址(压缩 C) | 146ZNjH7XTXnMd1ofe3z7CL2i8WM7N2UqT |
| P2PKH 地址(非压缩 U) | 16kR3eiswUY6tmGnxZhwQmW6LAvTLGWG2G |
请留意最后两行:同一个私钥,两个完全不同的地址。本页的结果面板会实时重算以上全部值, 你可以自己验证这个结论。
7. 压缩 vs 非压缩 —— 到底有什么区别
两种编码描述的是同一个点,签名上也都能用,但它们导向不同的地址,后果非常实际。
| 压缩(C) | 非压缩(U) | |
|---|---|---|
| 公钥长度 | 33 字节 | 65 字节 |
| hash160 的输入 | 02/03‖X | 04‖X‖Y |
| 得到的 P2PKH 地址 | 146ZNjH7…2UqT | 16kR3eis…WG2G |
| WIF 前缀 | K…/L…(载荷 33 字节 + 01 标志) | 5… |
| 花费时 scriptSig 大小 | 约 107 字节 | 约 139 字节 |
| 引入时间 | Bitcoin 0.6(2012 年) | 中本聪最初的客户端 |
| 现状 | 所有地方都在用 | 历史遗留,仅为找回旧资金而保留 |
私钥本身不知道自己「是不是压缩的」。这个标志位存在 WIF 字符串里,不在私钥里。 如果你把一个压缩 WIF 导入钱包,而币实际上躺在非压缩地址上(或者反过来),钱包就会去看错误的 地址、显示余额为零,然后你就以为钱没了。通常并没有丢 —— 但你需要一个能同时推导两种形式的钱包, 或者像本页这样的工具,才能查清楚币到底在哪个地址上。
8. 压缩公钥接下来会变成什么
- P2PKH(
1…):Base58Check(0x00 ‖ hash160),见 私钥 → P2PKH 压缩地址。 - P2WPKH(
bc1q…):bech32("bc", 0, hash160)。这里压缩公钥是 强制要求 —— witness 程序固定为 20 字节。 - P2SH-P2WPKH(
3…):Base58Check(0x05 ‖ hash160(0x0014 ‖ hash160))。 - P2TR(
bc1p…):完全另一条路径 —— 内部公钥先经TapTweak调整, 最终只使用输出公钥的 x 坐标。
9. 安全须知
- 公钥可以安全公开。在离散对数问题依然困难的前提下,它不会泄漏任何关于
k的可利用信息。 - 但公钥确实会泄漏一些信息:一旦某个密钥签过交易,该地址就与那个公钥公开绑定在一起。 万一发生 ECDSA 随机数复用,攻击者的搜索空间会因此变小。Taproot 的部分动机正是不再把公钥明晃晃地 暴露在链上。
- 收到公钥时务必做曲线方程校验(
Y² ≡ X³ + 7)。接受曲线外的点会打开 无效曲线攻击的大门,足以恢复出私钥。本页会做这项校验,并拒绝曲线外的输入。 - 量子计算机若运行 Shor 算法,可以直接解出离散对数。受威胁的是已经暴露在链上的公钥,而不是 从未花费过的、基于哈希的地址。
10. 常见错误
- 把私钥和 WIF 混为一谈。 WIF 是私钥的 Base58Check 编码,额外带上网络字节与 压缩标志。同一个秘密,两者是不同的字符串。
- 以为「一个私钥 = 一个地址」。 一个私钥至少能导出五类不同地址,外加测试网变体。
- 把公钥当成地址。 公钥本身不会被直接 Base58 成地址;地址是(编码后的)公钥的 20 字节哈希。
- 补零错误。 私钥
0x1f是 31 个前导零字节再加1f;丢掉这些零 会得到一个完全不同的私钥。 - 把 p 和 n 搞混。
X、Y是对p取模;k是对n取模。
11. 速查表
| 项目 | 值 |
|---|---|
| 公式 | secp256k1 上的 P = k · G |
| 输入长度 | 32 字节(接受任意长度,自动左补零) |
| 输出长度 | 压缩 33 字节 / 非压缩 65 字节 |
| 压缩前缀 | 02 = Y 为偶,03 = Y 为奇 |
| 解压缩公式 | Y = (X³+7)^((p+1)/4) mod p,再按奇偶性修正 |
| 可逆吗 | 不可逆 —— 这是单向函数(ECDLP) |
| 能公开吗 | 可以,公钥本身就是用来公开的 |