椭圆曲线加密:ECDH 和 ECDSA

原文: Key pair generation and two ECC algorithms: ECDH and ECDSA

椭圆曲线参数(Domain parameters)

椭圆曲线加密算法工作在 有限域上的椭圆曲线 的循环子群上,曲线的参数可以用一个六元组 \((p, a, b, G, n, h)\) 表示:

  • 素数 \(p\) 指定有限域的大小。

  • 椭圆曲线的系数 \(a\)\(b\)

  • 子群的 基点 \(G\)

  • 子群的 \(n\)

  • 子群的 cofactor \(h\)

椭圆曲线加密(Elliptic Curve Cryptography)

椭圆曲线的核心原理如下:

  1. 私钥(private key) 是从 \({1, \cdots, n - 1}\) 中随机选择的一个数 \(d\)\(n\) 是子群的序)。

  2. 公钥 是点 \(H = dG\)\(G\) 是子群的基点)。

根据循环子群的特性,知道 \(d\)\(G\) 可以很容易的计算得到 \(H\),相反,知道 \(H\)\(G\) 想要得到私钥 \(d\) 非常的困难,因为这是一个离散对数问题。

椭圆曲线常用的有以下两个算法:用于加密的 ECDH (Elliptic curve Diffie-Hellman) 算法,用于计算数字签名的 ECDSA (Elliptic Curve Digital Signature Algorithm) 算法。

ECDH 加密算法

ECDH 是 Diffie-Hellman 算法 的一个变种,它本质上是 密钥协商算法 ,只负责通信双方密钥的生成和交换,如何使用这个密钥加密数据取决于用户,和 ECDH 算法无关。

假设 Alice 和 Bob 想要交换信息,下面是使用 ECDH 交换密钥的过程:

  1. 首先,Alice 和 Bob 各自生成自己的公钥和私钥。设 Alice 的私钥为 \(d_A\) ,公钥 \(H_A = d_AG\),Bob 的私钥 \(d_B\),公钥 \(H_B = d_BG\) 。两人使用同样的曲线参数。

  2. Alice 和 Bob 在不可靠的信道上交换它们的公钥。中间人即使监听获得了两人的公钥 \(H_A\)\(H_B\) ,也没办法解出两人的私钥 \(d_A\)\(d_B\) ,因为需要解离散对数问题。

  3. Alice 计算 \(S = d_AH_B\) (使用自己的私钥和 Bob 的公钥),Bob 计算 \(S = d_BH_A\) (使用自己的私钥和 Alice 的公钥)。这个 \(S\) 是两人之间“共同的秘密(shared secret)”。

\[S = d_AH_B = d_A(d_BG) = d_B(D_AG) = d_BH_A\]

通过公钥 \(H_A\)\(H_B\) 很难解出 \(S\) 来,这个叫做 Diffie-Hellman 难题。即:

给定三个点 \(P\)\(aP\)\(bP\),求 \(abP\)

或者等价的(原始 Diffie-Hellman 算法里用的):

给定三个整数 \(k\)\(k^x\)\(k^y\),求 \(k^{xy}\)

../_images/ecdh.png

Diffie-Hellman 难题更详细的可以参见可汗学院的这个视频: Public key cryptography - Diffie-Hellman Key Exchange (full version)

解决椭圆曲线的 Diffie-Hellman 难题需要解决离散对数难题,所以很难求解。

Alice 和 Bob 获得共同的秘密 \(S\) 后,就可以使用对称加密来交换数据了。

比如,可以使用 \(S\)\(x\) 坐标作为密钥,使用 AES 或者 3DES 之类的算法来加密信息。TLS 的方式比这个稍微复杂一点,它用的是 \(x\) 坐标再加上一些和连接相关的数值后计算的一个 hash。

ECDHE 加密算法

ECDHE 指的是 Ephemeral(临时的) ECDH 算法,也就是说 ECDH 加密算法交换的密钥只是临时的,不是 静态 的。

比如 TLS 就使用了 ECDHE 算法,客户端和服务端每次建立连接的时候都会生成并交换新的公私钥。

ECDSA 签名算法(Signing with ECDSA)

ECDSA 签名算法的使用场景是这样的:Alice 要发送一个消息给 Bob,为了让 Bob 相信这个信息确实是她发出的, Alice 使用自己的私钥 \(d_A\) 给消息生成一个数字签名并和消息一起发给 Bob,Bob 收到后可以使用 Alice 的公钥 \(H_A\) 验证这个消息是否确实是 Alice 所发。

这里两人依然使用同样的椭圆曲线参数。ECDSA 是 Digital Signature Algorithm 在椭圆曲线上的应用。

ECDSA 是用消息的 hash 来生成签名的,这个 hash 函数我们可以自己定(但最好使用一个 安全 hash 算法 )。hash 值会被截断到和子群的序 \(n\) 同样的 bit 长度。记这个截断后的整数值为 \(z\)

ECDSA 算法签名的过程如下:

  1. \({1, \cdots, n - 1}\) 随机取一个数 \(k\)\(n\) 是子群的序)。

  2. 计算点 \(P = kG\)\(G\) 是子群的基点)。

  3. 计算 \(r = x_P \bmod n\)\(x_P\)\(P\) 点的 \(x\) 坐标)。

  4. 如果 \(r = 0\) 换一个 \(k\) 后重试。

  5. 计算 \(s = k^{-1}(z + rd_A) \bmod n\) (其中 \(d_A\) 是 Alice 的私钥, \(k^{-1}\)\(k\) 的乘法逆元。

  6. 如果 \(s = 0\),换一个 \(k\) 后重试。

\((r, s)\) 对就是生成的数字签名。

../_images/ecdsa.png

简言之,这个算法生成一个密钥 \(k\) ,然后使用点的乘法将其藏入 \(r\) 中(乘法容易,反过来就是对数问题,求解很困难),最后使用公式 \(s = k^{-1}(z + rd_A) \bmod n\)\(r\) 和消息的 hash \(z\) 绑定。

验证签名(Verifying signatures)

Bob 收到消息后,同 Alice 计算签名时算消息的 hash 的方法一样计算收到的消息的 hash 值 \(z\) 。然后:

  1. 计算 \(u_1 = s^{-1}z \bmod n\)

  2. 计算 \(u_2 = s^{-1}r \bmod n\)

  3. 计算点 \(P = u_1G + u_2H_A\)

如果 \(r = x_P \bmod n\) ,那么说明这条消息确实是 Alice 所发。

算法证明(Correctness of the algorithm)

根据公钥定义 \(H_A = d_AG\)\(d_A\) 是私钥),我们可以得到:

\[\begin{split}\begin{array}{rl} P & = u_1 G + u_2 H_A \\ & = u_1 G + u_2 d_A G \\ & = (u_1 + u_2 d_A) G \end{array}\end{split}\]

根据上面 \(u_1\)\(u_2\) 点定义:

\[\begin{split}\begin{array}{rl} P & = (u_1 + u_2 d_A) G \\ & = (s^{-1} z + s^{-1} r d_A) G \\ & = s^{-1} (z + r d_A) G \end{array}\end{split}\]

为了简洁,上面的公式都省略了“ \(\bmod n\)”。

又: \(s = k^{-1}(z + rd_A) \bmod n\) ,两边乘上 \(k\) 再除 \(s\) 得到: \(k = s^{-1}(z + rd_A) \bmod n\) 。将上面 \(P\) 公式里的 \(s^{-1}(z + rd_A)\) 替换为 \(k\) 得到:

\[\begin{split}\begin{array}{rl} P & = s^{-1} (z + r d_A) G \\ & = k G \end{array}\end{split}\]

这个公式和生成签名第 2 步里的公式一模一样,也就是生成和验证的时候我们可以计算得到相同的点 \(P\)。证明完毕。

ECDSA 使用的密钥 \(k\) 应该不可预测,如果我们所有的签名使用同样的 \(k\) 或者使用的随机数生成器可预测,攻击者可以有办法破解得到私钥。