ECC密钥交换

椭圆曲线密码学(ECC)的数学核心非常简洁:它把密码运算归结为有限域上的点加法与倍点运算,然后用这些点的坐标构建公钥、私钥和共享密钥。为了理解 ECC,我们可以先看一条简单的椭圆曲线
y2≡x3+ax+b (mod p)
这里的 𝑝 p 是模数,所有计算都在有限域 𝐹 𝑝 F p 内进行,超过 𝑝 p 的结果会“折回”到 0 到 𝑝 − 1 p−1 之间
在 ECC 中,我们把曲线上的点记作 𝑃 = ( 𝑥 , 𝑦) P=(x,y)。点加法 𝑃 + 𝑄 P+Q 的定义如下:
- 普通加法(P ≠ Q)
画一条直线穿过 P 和 Q,它会再交曲线一个点 R′。点加法结果是 R′ 关于 x 轴的镜像。公式化为有限域运算:m = (y2 - y1) / (x2 - x1) mod p x3 = m^2 - x1 - x2 mod p y3 = m*(x1 - x3) - y1 mod p - 倍点运算(P = Q)
当 P = Q 时,直线退化为通过 P 的切线,斜率变为:再用同样公式计算 R = 2Pm = (3*x1^2 + a) / (2*y1) mod p - 相反点(P = -Q)
当两个点在竖直线上时,它们纵坐标互为负数,横坐标相同,点加法结果定义为“无穷远点 O”,在程序里通常用 (nil, nil) 表示
Note
注意,在有限域里“除法”不能直接做,所以公式里的除法都是通过模逆实现的:a / b mod p = a * b^(-1) mod p,对应 Go 里的 ModInverse
有了点加法和倍点运算,就可以实现标量乘法(scalar multiplication):计算 𝑘*𝑃 kP,也就是 P 加自己 k 次。Go 代码里通过二进制展开、循环翻倍加法高效实现
for i := k.BitLen()-1; i>=0; i-- {
result = Add(result, result) // 翻倍
if k.Bit(i) == 1 {
result = Add(result, addend) // 累加
}
}这是 ECC 的核心运算。它可以用来生成公钥和私钥:选择一个随机私钥 k,然后计算公钥 K = kG(G 是曲线上的基点)。因为有限域上的离散对数问题很难解,已知 G 和 K 几乎不可能反推出 k,从而保证安全性。
基于这个原理,ECC 可以实现密钥交换(ECDH)、数字签名(ECDSA、EdDSA)等。在你提供的 Go 代码里:
- Alice 和 Bob 各自生成私钥(aPriv, bPriv)
- 通过标量乘法计算公钥(aPub, bPub)
- 最终共享密钥通过对方公钥再乘自己私钥得到(shared1, shared2),两者相等
整个逻辑核心就是:通过有限域上的点加法和倍点运算不断求出新点 R,然后利用 R 的坐标生成公钥、共享密钥或签名。理解了点加法公式、倍点运算和模逆
package main
import (
"fmt"
"math/big"
)
// 定义椭圆曲线 y^2 = x^3 + ax + b (mod p)
type Curve struct {
P *big.Int // 模数
A *big.Int
B *big.Int
G Point
}
type Point struct {
X, Y *big.Int
}
// 是否为无穷远点
func (p Point) IsInfinity() bool {
return p.X == nil && p.Y == nil
}
// 椭圆曲线加法 P + Q
func (c Curve) Add(p1, p2 Point) Point {
if p1.IsInfinity() {
return p2
}
if p2.IsInfinity() {
return p1
}
p := c.P
var m *big.Int
if p1.X.Cmp(p2.X) == 0 && p1.Y.Cmp(p2.Y) != 0 {
// P + (-P) = O
return Point{nil, nil}
}
if p1.X.Cmp(p2.X) == 0 && p1.Y.Cmp(p2.Y) == 0 {
// P + P
two := big.NewInt(2)
num := new(big.Int).Mul(two, p1.Y)
num.Mod(num, p)
den := new(big.Int).Mul(big.NewInt(3), new(big.Int).Mul(p1.X, p1.X))
den.Add(den, c.A)
den.Mod(den, p)
m = new(big.Int).ModInverse(den, p)
m.Mul(m, num)
} else {
// P + Q, P != Q
num := new(big.Int).Sub(p2.Y, p1.Y)
num.Mod(num, p)
den := new(big.Int).Sub(p2.X, p1.X)
den.Mod(den, p)
m = new(big.Int).ModInverse(den, p)
m.Mul(m, num)
}
m.Mod(m, p)
// x3 = m^2 - x1 - x2
x3 := new(big.Int).Mul(m, m)
x3.Sub(x3, p1.X)
x3.Sub(x3, p2.X)
x3.Mod(x3, p)
// y3 = m*(x1 - x3) - y1
y3 := new(big.Int).Sub(p1.X, x3)
y3.Mul(m, y3)
y3.Sub(y3, p1.Y)
y3.Mod(y3, p)
return Point{x3, y3}
}
// 倍点运算 k*P
func (c Curve) ScalarMult(p Point, k *big.Int) Point {
result := Point{nil, nil} // 初始化无穷远点
addend := p
for i := k.BitLen() - 1; i >= 0; i-- {
result = c.Add(result, result) // 翻倍
if k.Bit(i) == 1 {
result = c.Add(result, addend)
}
}
return result
}
func main() {
// 定义小曲线示例 y^2 = x^3 + 2x + 3 mod 97
p := big.NewInt(97)
a := big.NewInt(2)
b := big.NewInt(3)
G := Point{big.NewInt(3), big.NewInt(6)}
curve := Curve{P: p, A: a, B: b, G: G}
// Alice 的私钥
aPriv := big.NewInt(20)
aPub := curve.ScalarMult(G, aPriv)
// Bob 的私钥
bPriv := big.NewInt(15)
bPub := curve.ScalarMult(G, bPriv)
fmt.Println("Alice 公钥:", aPub)
fmt.Println("Bob 公钥:", bPub)
// 计算共享密钥
shared1 := curve.ScalarMult(bPub, aPriv)
shared2 := curve.ScalarMult(aPub, bPriv)
fmt.Println("Alice 共享密钥:", shared1)
fmt.Println("Bob 共享密钥:", shared2)
}