Skip to content

EC基本方案 ​

ECDH(密钥交换) ​

密钥生成 ​

私钥生成 ​

私钥 d 是一个随机整数,满足:

1≤d≤n−1

公钥生成 ​

公钥 Q 通过标量乘法计算:

Q=d⋅G

密钥交换 ​

Alice 和 Bob 通过以下步骤交换密钥:

  1. Alice 生成私钥 dA 和公钥 QA=dA⋅G,将 QA 发送给 Bob。
  2. Bob 生成私钥 dB 和公钥 QB=dB⋅G,将 QB 发送给 Alice。
  3. Alice 接收到 QB 后,计算共享密钥:KA=dA⋅QB=dA⋅(dB⋅G)=(dA⋅dB)⋅G
  4. Bob 接收到 QA 后,计算共享密钥:KB=dB⋅QA=dB⋅(dA⋅G)=(dB⋅dA)⋅G
  5. 最终得到相同共享密钥 K=(dA⋅dB)⋅G。

ECIES(公钥加密) ​

密钥生成 ​

私钥生成 ​

私钥 d 是一个随机整数,满足:

1≤d≤n−1

公钥生成 ​

公钥 Q 通过标量乘法计算:

Q=d⋅G

加密过程 ​

给定消息 M 和公钥 Q,ECIES加密过程如下:

  1. 生成随机数 k,满足 1≤k≤n−1。
  2. 计算点 P=k⋅G=(x1,y1)。
  3. 计算公钥 K=k⋅Q=k⋅d⋅G。
  4. 生成对称密钥 Ksym=H(K),其中 H 是哈希函数。
  5. 加密消息 C=M⊕Ksym。
  6. 输出密文 (C,P)。
  7. 发送密文 (C,P) 给接收方。

解密过程 ​

给定密文 (C,P) 和私钥 d,ECIES解密过程如下:

  1. 计算共享密钥 K=d⋅P=d⋅k⋅G。
  2. 生成对称密钥 Ksym=H(K)。
  3. 解密消息 M=C⊕Ksym。
  4. 输出明文 M。

ECDSA(签名) ​

域参数 ​

  • p:有限域的大小
  • a,b:椭圆曲线参数
  • G:基点(生成元)
  • n:基点的阶(即 n⋅G=O)

密钥生成 ​

私钥生成 ​

私钥 d 是一个随机整数,满足:

1≤d≤n−1

公钥生成 ​

公钥 Q 通过标量乘法计算:

Q=d⋅G

其中 G 是椭圆曲线的基点。

签名生成 ​

给定消息的哈希值 h 和私钥 d,ECDSA签名过程如下:

步骤1:生成随机数 ​

生成一个随机数 k,满足:

1≤k≤n−1

!!!:这里是简化的,具体参照这里,简单说就是Hash(h||d)

步骤2:计算点 ​

计算椭圆曲线点:

P=k⋅G=(x1,y1)

步骤3:计算 r ​

r=x1modn

!!!:取点 P 的 x 坐标模 n,作为签名的第一部分。如果 r=0,则重新选择 k。

步骤4:计算 s ​

s=k−1(h+r⋅d)modn

!!!:

  • k−1 是 k 在模 n 下的乘法逆元
  • (h+r⋅d) 将消息哈希与私钥信息结合
  • 整个表达式确保了签名与私钥和消息的绑定关系

如果 s=0,则重新选择 k。

步骤5:输出签名 ​

签名为 (r,s)。

签名验证 ​

给定消息哈希 h、签名 (r,s) 和公钥 Q,验证过程如下:

步骤1:参数验证 ​

验证 r 和 s 在有效范围内:

1≤r≤n−11≤s≤n−1

步骤2:计算逆元 ​

计算 s 的乘法逆元:

w=s−1modn

步骤3:计算标量 ​

u1=h⋅wmodnu2=r⋅wmodn

步骤4:计算验证点 ​

P′=u1⋅G+u2⋅Q=(x1′,y1′)

步骤5:验证结果 ​

如果 x1′modn=r,则签名有效;否则无效。

数学原理 ​

从签名生成有:

s=k−1(h+r⋅d)modn

重排得到:

k=s−1(h+r⋅d)modn(α)

在验证过程中:

P′=u1⋅G+u2⋅Q

替换 u1 和 u2:

P′=(h⋅s−1)⋅G+(r⋅s−1)⋅Q

由于 Q=d⋅G:

P′=(h⋅s−1)⋅G+(r⋅s−1)⋅(d⋅G)P′=s−1(h+r⋅d)⋅G

根据签名生成中(式α)的关系:

P′=k⋅G=P

这证明了验证点 P′ 的 x 坐标确实等于 r,从而验证了签名的有效性。

Add-On: v的生成与公钥恢复 ​

由于压缩传输空间的需要以及ECDSA可以从签名恢复公钥的性质,往往只传输 (r,s) ,而不传输公钥 Q 。

在以太坊中,需要一个 v(也叫 recovery id,记作 recId)与 (r,s) 一起传输,使得验证者可以从签名中恢复出公钥 Q(进而得到地址)。

1) v/recId 到底在表达什么 ​

ECDSA 签名里有中间点:

R=k⋅G=(xR,yR)

而签名的

r≡xR(modn)

因此仅凭 r 并不能唯一确定 R:

  • 同一个 x 往往对应两个点(y 与 −y),需要 1 bit 表示 y 的奇偶性(或“是否取较大/较小根”)。
  • 由于 xR 是在有限域 Fp 上的坐标,而 r 是模 n 的结果,可能存在xR=r+jn(j∈{0,1})需要再用 1 bit 表示 j。

所以常见的恢复标识是:

recId∈{0,1,2,3}

其中通常可理解为:

  • recIdmod2:选择 y 的奇偶性(对应两种 R)
  • ⌊recId/2⌋:表示是否使用 x=r+n(即 j)

2) 以太坊里的 v ​

历史上(黄皮书/早期实现)以太坊把 recId 平移为:

v∈{27,28},v=recId+27(recId∈{0,1})

引入 EIP-155(防重放)后,交易签名里常见:

v=recId+35+2⋅chainId

因此从交易的 v 还原 recId 通常是:

  • 若 v∈{27,28}:recId=v−27
  • 否则(EIP-155):recId=v−(35+2⋅chainId)

备注:不同库/链的编码细节可能略有差异,但核心都是携带“恢复所需的那几 bit”。


3) 从 (r,s,v) 恢复公钥 Q ​

ECDSA 有:

s≡k−1(h+rd)(modn)

两边乘以 k:

sk≡h+rd(modn)

又因为 R=kG,于是:

sR=s(kG)=(sk)G≡(h+rd)G=hG+r(dG)=hG+rQ

移项得到:

rQ≡sR−hG(modn)

从而:

Q≡r−1(sR−hG)(modn)

这就是“公钥恢复”的核心:只要能确定 R,就能算出 Q。
而 v/recId 的作用,就是帮助从 r 选出正确的 R。


4) 具体步骤 ​

给定曲线参数 (p,a,b,G,n)、消息哈希 h、签名 (r,s) 与 recId:

  1. 范围检查1≤r,s≤n−1
  2. 由 r 构造候选 x
    令 j=⌊recId/2⌋∈{0,1},取x=r+jn若 x≥p 则该候选无效(这一分支失败)。
  3. 在曲线上解出点 R
    计算y2≡x3+ax+b(modp)若无解则该候选无效;若有两解 ±y,用 recIdmod2 决定取哪个(例如选与奇偶性匹配的那个),得到R=(x,y)
  4. 计算公钥Q=r−1(sR−hG)得到候选公钥 Q。
  5. 校验
    用恢复出来的 Q 对 (r,s) 做一次标准 ECDSA 验签;通过则恢复成功,否则尝试其他候选(在实现里就是不同的 recId 分支)。