Private Key Tools

secp256k1 椭圆曲线点运算

把两个曲线点相加、对一个点做点倍,或把生成元乘以一个标量 —— 每一个比特币公钥都建立在这套最原始的群运算之上。用 k·G 看私钥如何变成一个点;把一个点与它的相反数相加,看它如何塌缩成无穷远点。

mul 用标量乘固定的生成元 G;add 同时使用两个操作数;double 忽略操作数 B,计算 2·A。
mul 模式下 A 是标量 —— 一个落在 [1, n−1] 内的十六进制私钥(占位符就是如此)。add 与 double 模式下 A 是一个点:字母 G 表示生成元,也可以填压缩(66 位 hex)、非压缩(130 位 hex)或 x-only(64 位 hex)公钥。标量不足 64 位会自动左补 0。
第二个点,仅在 add 模式使用;mul(永远乘 G)与 double 会忽略它。留空表示生成元 G。想看无穷远点,就把一个公钥与「同一 x 坐标但 02/03 前缀相反」的公钥相加。
还没有输入

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

技术原理详解

本页计算了什么

这是一个直接操作比特币所用椭圆曲线 secp256k1 群运算的「裸机」试验台。它只提供三种运算:

mul:R = k · G    add:R = P + Q    double:R = P + P
操作数(标量 / 点)→ y² = x³ + 7 (mod p) 上的群运算→ 结果点 (x₃, y₃)→ 02/03 ‖ x 或 04 ‖ x ‖ y

全部计算都在本服务器上用纯 PHP + GMP 大整数实时完成。输出包括:按解析结果展示的操作数、实际用到的曲线常量、 结果点、它的奇偶性、两种标准序列化形式、结果是否为无穷远点,以及对结果独立执行的曲线方程校验。

1. 曲线,以及为什么它的点能构成群

secp256k1 是所有满足下面方程、坐标取模素数 p 的 (x, y) 对

y² ≡ x³ + 7  (mod p)

再加上一个额外的元素——无穷远点 ∞——所组成的集合。由于方程是模 p 的, 它在纸上并不是一条光滑的弧线,而是散布在 256 位宽有限网格上约 n 个离散点。真正的奇迹在于:可以在这些 点上定义一种加法,它满足结合律、交换律,有单位元(∞),并且每个点都有逆元 −P。 凡是满足这四条性质的集合就叫做阿贝尔群,而群正是「可以把一个运算反复迭代」所需要的结构—— 「乘以一个整数」的全部含义就在于此。

逆元是免费的:如果 (x, y) 在曲线上,那么 (x, −y) 也在曲线上,因为曲线方程里只有 y²。所以 −P = (x, p − y)。注意 p − y 与 y 的奇偶性必然相反, 这一点正是压缩编码所利用的。

常量取值含义
域素数 p0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F坐标取值范围 [0, p−1]。等于 2²⁵⁶ − 2³² − 977,十进制为 115792089237316195423570985008687907853269984665640564039457584007908834671663。
群的阶 n0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141点的总数(含 ∞)。私钥取值范围 [1, n−1]。十进制为 115792089237316195423570985008687907852837564279074904382605163141518161494337。
生成元 Gx0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798约定俗成的基点。每一个比特币公钥都是它的倍数。
生成元 Gy0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8为偶数,所以 G 的压缩形式以 02 开头。
余因子 h1群的阶恰好是 n,且 n 是素数。见第 5 节。
p mod 43正因如此模平方根才有廉价公式:√a = a^((p+1)/4)。
p 和 n 是两个不同的数

这条曲线上几乎所有的初学者 bug 都来自把它们搞混:p 约束的是坐标,所有域运算都对 p 取模;n 约束的是标量,所有标量运算都对 n 取模。两者在数值上 都接近 2²⁵⁶,所以极易混淆;一旦用错,得到的结果仍然是「看起来合法的 32 字节数字」。

2. 群运算的几何解释:弦与切线

经典构造是在实数域上作图。取曲线上的两点 P、Q,作过它们的直线。三次曲线与一条直线 恰好交于三点(按重数计),于是有第三个交点 R。把 R 沿 x 轴翻转,就定义为和:

P ⊕ Q := −R,其中 直线(P, Q) ∩ 曲线 = { P, Q, R }     2P := −T,其中 切线(P) ∩ 曲线 = { P, T }

为什么要翻转?因为曲线上的点关于 x 轴对称后仍在曲线上,而且如果不翻转,这个构造就不满足结合律—— 只有翻转后的版本才能让 (P+Q)+R = P+(Q+R) 成立。有三种特殊情况值得单独命名,因为它们正好是 实现里必须分支的地方:

  1. P = Q。 过一个点的直线不唯一,极限情形就是该点处的切线。这就是「弦与切线」里的 「切线」,也是点倍运算所做的事。
  2. Q = −P(x 相同、y 相反)。过这两点的直线是竖直的,它与曲线只交于这两点,第三个交点就是 无穷远点。所以 P + (−P) = ∞,完全符合「加法逆元」的定义。
  3. P = ∞ 或 Q = ∞。 既然 ∞ 是单位元,和就等于另一个操作数。

在有限域上,「过 P、Q 的直线」依然有意义——它是满足某个一次方程(模 p)的点集——同样的 「三交点」论证依然成立。图不好画了,但代数毫发无损。

3. 群运算的代数解释

模 p 下除以一个非零元素,等于乘以它的模逆元,所以下面每一个「斜率」都是真实可计算的域元素。 设 λ 为过两点的弦(或切线)的斜率,结果点为 (x₃, y₃):

点加(P ≠ ±Q):λ = (y₂ − y₁) / (x₂ − x₁)     点倍(P = Q):λ = (3x₁²) / (2y₁)
x₃ = λ² − x₁ − x₂     y₃ = λ(x₁ − x₃) − y₁    (全部 mod p)

整套群运算就这些公式。x₃ 只依赖 λ 和两个 x 坐标,而 y₃ 随后由 「关于直线翻转」唯一确定。还要注意:公式关于 x₁ ↔ x₂ 对称,但 y 项不对称——这正是 P + Q = Q + P 成立、而 P + (−P) 必须单独处理的原因。

一次点加的成本是 1 次模逆元(最贵的部分:本实现用费马小定理做幂运算 a^(p−2))加上 2–3 次模乘/模平方。 真正的库会改用射影坐标或雅可比坐标来「推迟」求逆,用更多乘法换掉逆元,因为一次逆元的代价大约是乘法的上百倍。

情形条件使用的公式结果
一般点加x₁ ≠ x₂弦斜率 (y₂−y₁)/(x₂−x₁)一个普通曲线点
点倍P = Q(即 x₁ = x₂、y₁ = y₂)切线斜率 3x₁²/(2y₁)一个普通曲线点
互逆点对x₁ = x₂ 且 y₁ + y₂ ≡ 0回避除零∞,单位元
单位元P = ∞ 或 Q = ∞直接短路另一个操作数
y = 0点倍时 y₁ = 02y₁ ≡ 0∞;secp256k1 上不存在这种点

4. 为什么 ∞ 是单位元,以及它何时出现

无穷远点不是一对坐标,也无法被序列化成公钥——标准编码里根本没有它的位置。它存在的意义是回答 「当直线竖直时第三个交点是什么」,而它的行为与普通算术中的零完全一致:

P + ∞ = ∞ + P = P    ∞ + ∞ = ∞    n · G = ∞

有三种情形会产生它,本页会明确显示「结果是否为无穷远点」:

5. 余因子为 1、循环群,以及为什么每个公钥都是 G 的倍数

曲线上的点数等于 h · n,其中 h 是余因子。secp256k1 的 h = 1, 所以群的阶就是 n,而 n 是素数。这带来几个非常舒服的推论:

6. 倍点-加点:k · G 到底怎么算出来

标量乘法没有闭式捷径,所以实现只能逐位迭代标量。本项目里的版本从最高位到最低位遍历 256 个比特:

R = ∞                          # 单位元
for bit in bits_of(k):         # 256 位标量 → 256 次循环
    R = double(R)              # R = R + R
    if bit == 1:
        R = add(R, G)          # R = R + G
return R                       # R = k · G

成本是每位一次点倍,外加每个为 1 的比特一次点加。演示私钥 0824c314…171ca 的比特普查结果是: 256 位中有 116 位为 1,因此循环执行 256 次点倍 + 116 次点加——共 372 次点运算,而每一次内部又是 一次求逆加若干次乘法。生产级实现使用窗口法(预先算出 G, 2G, …, 15G,一次处理 4 位)把次数减少约三分之一; 当标量必须保密时则改用 Montgomery 阶梯。

运算点运算次数(256 位标量 / 单次调用)主要开销
double —— 2 · P1 次点倍1 次求逆 + 约 2 次平方 + 约 2 次乘法
add —— P + Q(P ≠ ±Q)1 次点加1 次求逆 + 约 3 次乘法/平方
add —— P + (−P)0(命中分支)只比较坐标;直接返回 ∞
mul —— k · G256 次点倍 + (k 的 popcount)次点加每一步都要求逆,合计约数百次
mul —— k · G,窗口法(真实库)约 64 次点倍 + 约 64 次点加 + 预计算同样开销,但次数少 2–3 倍
为什么反方向是不可能的

从 k 到 k·G 只要 256 次迭代。但从点反推标量就是椭圆曲线离散对数问题; 目前最通用的算法(Pollard rho)需要大约 √n ≈ 2¹²⁸ 步量级。正是这种「正向很便宜、反向贵得离谱」的 不对称性,让公钥可以公开。

7. 时间侧信道,以及这里的「常数时间」是什么意思

如果标量是真的私钥,上面的伪代码就是一个安全隐患,因为它的控制流依赖秘密:比特为 0 就跳过点加, 为 1 就执行点加。任何能测量签名或派生耗时、或观察芯片功耗的人,都可以一个比特一个比特地把标量还原出来。 这不是纸上谈兵:对朴素 ECDSA 实现的计时攻击已经成功跨网络恢复过私钥。

常数时间的意思是:执行的操作序列与内存访问模式不依赖秘密值——只有数据依赖。具体做法包括: 无论如何都同时执行点倍与点加,然后用算术方式挑选正确结果;使用没有特殊分支的统一加法公式;标量盲化 (对随机 r 计算 (k + r·n)·G,点不变但比特模式变了);以及点盲化。libsecp256k1 综合使用了 其中若干手段,并且专门为了避免依赖秘密的分支而编写。

本页是教学工具,不是钱包

本页背后的纯 PHP + GMP 实现不是常数时间的:GMP 大数的运算时间依赖数据,倍点-加点循环会按标量比特 分支,也没有做盲化。用它试验本页这样的公开输入完全安全;但绝不能用它处理保护真实资金的私钥。那件事请交给 硬件钱包或经过充分验证的常数时间库。

8. 实例演算:对 G 做点倍

任何 secp256k1 实现最经典的健全性检查就是 2G。用上面的真实常量代入切线公式,本页算出:

步骤值
起点G = (79be667e…f81798,483ada77…10d4b8)
斜率 λ = 3Gx² / (2Gy) mod p约化为一个 256 位域元素
x₃ = λ² − 2Gxc6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5
y₃ = λ(Gx − x₃) − Gy1ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a
y₃ 的奇偶性偶数 → 压缩前缀 02
2G 压缩公钥02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5
2G 非压缩公钥04c6047f…b95c709ee51ae168…50cfe52a(65 字节)
独立验证把运算切换为 double 且 A = G:本页会对生成元做点倍,再额外执行 pointMul(2, G) 做交叉验证,并报告两者一致 —— 它们就是私钥 2 的公钥
hash160(2G) / P2PKH06afd46bcdfd22ef94ac122aa11f241244a37ecc → 1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP
私钥 2 的 WIFKwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU74NMTptX4(压缩)

另外两个可以用上方表单复现的检查,它们正好验证第 5 节的恒等式:

9. 常见错误

10. 速查表

项目值
曲线y² = x³ + 7,定义在 GF(p) 上,p = 2²⁵⁶ − 2³² − 977
群的阶n(素数,256 位),余因子 h = 1
单位元∞ —— 没有坐标,无法序列化
逆元−P = (Px, p − Py);会翻转 02/03 前缀
点加斜率λ = (y₂ − y₁)(x₂ − x₁)⁻¹ mod p
点倍斜率λ = 3x₁²(2y₁)⁻¹ mod p
结果x₃ = λ² − x₁ − x₂,y₃ = λ(x₁ − x₃) − y₁(均 mod p)
标量乘成本256 次点倍 + k 的每个为 1 的比特一次点加
公钥k · G,序列化为 02/03 ‖ x(33 字节)或 04 ‖ x ‖ y(65 字节)
合法标量1 ≤ k ≤ n−1;用 Btc::normalizePrivateKey 校验