小白也能看懂的 ECC 椭圆曲线加密算法

背景 : ECC 是一种极其巧妙的非对称加密算法, 其完美利用了 椭圆曲线几何累加 不可逆的性质,拥有 密钥体积小,计算速度快的优势,被广泛用于各种区块链,移动端APP的认证过程。 此文章致力于用最少的数学公式解释 ECC 椭圆曲线加密算法 ,包括椭圆曲线加法性质,进而推导到非对称加密,安全性分析, 最后再推导到 有限域上的椭圆曲线加密算法.

什么是 椭圆曲线

x, y 符合以下定义的曲线就是 椭圆曲线:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> y 2 = x 3 + a x + b , 且 4 a 3 + 27 b 2 ≠ 0 y^2 = x^3 + a x +b , 且 4 a^3 +27 b^2 \neq 0 </math>y2=x3+ax+b,且4a3+27b2=0

它可以是下面的各种形状:

椭圆曲线 上的加法

接上面, 椭圆曲线长上面那种样子都无所谓,我们这里不care它的形状的, 我们来讨论在椭圆曲线上面做加法

假设有一个点P 和 点Q, 那么我们定义 P + Q 为 : 经过 P,Q 两点的直线与 椭圆曲线的交点, 再将这个交点做 关于 X轴的对称, 关于 x 轴的对称点 就是 P+Q 。 比如下面这幅图:

聪明的你可能会问如果P,Q两点重合怎么办, 重合的话 就是 P+P =2P, 也就是 P点的切线 与椭圆曲线的交点, 再将这个交点做 关于 X轴的对称, 关于 x 轴的对称点 就是 2P.

椭圆曲线上 两点相加计算公式

如果有两个点 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x 1 , y 1 ) (x_1, y_1) </math>(x1,y1) , <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x 2 , y 2 ) (x_2, y_2) </math>(x2,y2), 那么 两点相加得到 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x 3 , y 3 ) (x_3, y_3) </math>(x3,y3) 公式如下:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> x 3 = k 2 − x 1 − x 2 y 3 = k ∗ ( x 1 − x 3 ) − y 1 k = ( y 2 − y 1 ) / ( x 2 − x 1 ) , 当 x 1 ≠ x 2 时 k = ( 3 x 1 2 + a ) / 2 y 1 , 当 x 1 = x 2 时 x_3 = k^2 - x_1 -x_2 \newline y3 = k*(x_1 - x_3) - y_1 \newline k = (y_2-y_1)/ (x_2-x1), 当 x_1 \neq x_2 时 \newline k = (3 x_1^2 +a) / 2 y_1, 当 x_1 = x_2 时 </math>x3=k2−x1−x2y3=k∗(x1−x3)−y1k=(y2−y1)/(x2−x1),当x1=x2时k=(3x12+a)/2y1,当x1=x2时

上面这个公式其实就是 给定两点 求直线, 再求 直线与椭圆曲线的交点,然后关于 x 轴对称的结果, 计算过程非常复杂,需要解三次方程, 你只需要知道有这个公式即可

椭圆曲线 上的加法 推广到 乘法

有了 2P, 那么同样的方法 求出3P。 我们将 过 P点和2P点做一条直线, 这条直线与 椭圆曲线的交点的 关于 X轴 对称就是 3P, 比如下面这样:

依次类推, 我们可以画出 4P, 5P... NP, 只要不停地连接两点画直线 找出与 椭圆曲线的交点, 再关于 X轴对称即可。我们把这叫做 椭圆曲线上的乘法。

事实上计算NP并不是需要一步步进行计算, 比如计算 100P, 并不用计算 1P, 2P, 3P, ..., 99P. 我可以直接告诉你一个结论 那就是 椭圆曲线上的加法符合结合律。 符合结合律有什么好处呢?好处相当地大, 我把 计算100P展开来说:

100P = 50P+50P, 而 50P =25P+ 25P , 25P =1P +24P, 24P = 12P +12P, 12P=6P+6P, 6P=3P+3P, 3P=1P+2P, 2P=1P+1P, 也就是计算 100P ,我实际上并不需要做100次加法, 而只需要做 8次加法即可,每当 NP(N为偶数时), 都可以将 NP 拆成 两个 N/2 * P 之和, 所以椭圆曲线上的乘法 Q=k*P 的时间复杂度为 O(logK)。 记住这个结论, 非常重要 !!!

从 椭圆曲线上的乘法 推广到 非对称加密

你可能会问求 椭圆曲线上的乘法这有什么用呢?

这个问题就是 椭圆曲线的精妙之处了:

给定一条椭圆曲线一个起点P, 和大的质数数k, 我们可以很容易地找到 Q=kP点(时间复杂度为 O(logK))。但是反过来, 给出终点Q 和起点P ,让你找出 k, 你只有一次次地 从 k =1, k=2, ... k=N 去暴力尝试, 这个时间复杂度是 为 O(k) 级别的。

这个性质是不是很熟悉?正向容易计算, 逆向极其困难, 是不是和RSA有点像了? RSA 根据 私钥计算公钥很容易, 公钥反推私钥几乎不可能。

如果我们将 k 看作私钥, 那么 Q 就可以被看作公钥, 这就是 椭圆曲线非对称加密算法的原理, 椭圆曲线加密算法的强度保障就在于 给定P 和 k 计算 Q是否容易, 但反之 给定Q和P 想要计算 k 则是否困难

具体如何加解密:

  1. 选定一条椭圆曲线, 选定起点(也叫基点)P(通常选得非常大), 选取一个整数 k( k<n , k通常很大, 几百bit级别, 但同样的强度相对于 RSA 又明显小一个数量级) 作为私钥, 计算公钥 Q=kP
  2. 加密过程如下: 选取一个随机整数r ( r<n, r 通常也很大), 使用公钥Q 和 r 对 明文 <math xmlns="http://www.w3.org/1998/Math/MathML"> M M </math>M 进行加密 得到 <math xmlns="http://www.w3.org/1998/Math/MathML"> C i p h t e r T e x t CiphterText </math>CiphterText。 <math xmlns="http://www.w3.org/1998/Math/MathML"> C i p h e r T e x t Cipher_Text </math>CipherText 由两部分组成:

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> C i p h e r T e x t = [ C i p h e r T e x t 1 , C i p h e r T e x t 2 ] = [ r P , M + r Q ] 其中 M 为原始数据在椭圆曲线上的编码 , 也是一个坐标点 CipherText= [CipherText1, CipherText2] =[r P, M+r Q] \newline 其中 M 为 原始数据在 椭圆曲线上的 编码, 也是一个坐标点 </math>CipherText=[CipherText1,CipherText2]=[rP,M+rQ]其中M为原始数据在椭圆曲线上的编码,也是一个坐标点

  1. 解密过程如下:

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> M + r Q − K ( r P ) = M + k r P − k r P = M M+r Q -K (r P)= M+k r P-k r P= M </math>M+rQ−K(rP)=M+krP−krP=M

椭圆曲线非对称加密 安全性分析

  1. 椭圆曲线参数公开, 基点P公开, Q 公开, 私钥 k 解密方保密, r 为加密方保密(一次性随机数,不存储, 不具备重放性)
  2. <math xmlns="http://www.w3.org/1998/Math/MathML"> C i p h e r T e x t CipherText </math>CipherText 加密需要 公开的 Q, M ,以及 一次性随机数 r
  3. <math xmlns="http://www.w3.org/1998/Math/MathML"> C i p h e r T e x t CipherText </math>CipherText 解密需要 公开的 <math xmlns="http://www.w3.org/1998/Math/MathML"> C i p h e r T e x t CipherText </math>CipherText 以及 私钥 k
  4. 欲截获 <math xmlns="http://www.w3.org/1998/Math/MathML"> C i p h e r T e x t CipherText </math>CipherText 并破解 只有两种可能:
    1. 想办法获取到私钥 k, 若私钥不泄露则无此风险

    2. 直接通过 <math xmlns="http://www.w3.org/1998/Math/MathML"> M + r Q − r k P = M M+rQ-rkP= M </math>M+rQ−rkP=M, 此处由两重困难:

      1. k只能通过 终点Q 和 基点P 反推, 上面分析过极其困难

      2. r只能通过 终点 rP 和 基点P 反推, 也是极其困难的, 并且r 是一次性随机数, 不能被重放

从连续的椭圆曲线 推广到 有限域

根据上面的分析, 你已经知道了椭圆曲线从几何意义上是如果确保 密钥的强度了。复述一遍 就是 给定基点P 和 私钥 k, 计算终点 也就是 公钥 Q 比较容易;反之知道 终点Q和基点P ,想要反推密钥 k 将极其困难。

那么如果要在计算机上面使用椭圆曲线上的 密钥体系, 则还需要解决两个问题

  1. 将连续的曲线进行离散化处理,计算机是不能处理 1.111111111111111... 这种精度无穷大的小数的
  2. 将离散化的曲线个数进行有限化,计算机无法处理一个无穷大的数字的

解决方法也很简单,对症下药既可:

  1. 对连续的曲线进行取整, 对x只考虑整数
  2. 对曲线表达式两边同时进行 取模 操作, 取模操作约束了 等式左右两边的大小

如此一来, 离散且有限的"曲线" 表达式就变成下面这样:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> y 2 ≡ x 3 + a ∗ x + b ( m o d p ) , x ∈ Z 且 4 ∗ a 3 + 27 ∗ b 2 ≠ 0 y^2 \equiv x^3 + a*x + b (mod p) , x \in Z 且 4*a^3 +27*b^2 \neq 0 </math>y2≡x3+a∗x+b(modp),x∈Z且4∗a3+27∗b2=0

这条曲线记作 <math xmlns="http://www.w3.org/1998/Math/MathML"> G F ( p ) GF(p) </math>GF(p), 特别的是 p 一般取质数(实际应用中会取大质数)。 之所以取质数,是为了取值的多样性,为了取值的方碰撞性。

在 <math xmlns="http://www.w3.org/1998/Math/MathML"> G F ( p ) GF(p) </math>GF(p) 上的加法变成了这样:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> x 3 ≡ k 2 − x 1 − x 2 ( m o d p ) y 3 ≡ k ∗ ( x 1 − x 3 ) − y 1 ( m o d p ) k ≡ ( y 2 − y 1 ) / ( x 2 − x 1 ) ( m o d p ) , 当 x 1 ≠ x 2 时 k ≡ ( 3 ∗ x 1 2 + a ) / 2 ∗ y 1 ( m o d p ) , 当 x 1 = x 2 时 x_3 \equiv k^2 - x_1 -x_2 (mod p) \newline y3 \equiv k*(x_1 - x_3) - y_1 (modp)\newline k \equiv (y_2-y_1)/ (x_2-x1) (mod p), 当 x_1 \neq x_2 时 \newline k \equiv (3*x_1^2 +a) / 2*y_1 (mod p), 当 x_1 = x_2 时 </math>x3≡k2−x1−x2(modp)y3≡k∗(x1−x3)−y1(modp)k≡(y2−y1)/(x2−x1)(modp),当x1=x2时k≡(3∗x12+a)/2∗y1(modp),当x1=x2时

我们用 python 画一下 <math xmlns="http://www.w3.org/1998/Math/MathML"> y 2 ≡ x 3 + x + 1 ( m o d 23 ) y^2 \equiv x^3 + x +1 (mod 23) </math>y2≡x3+x+1(mod23)的图像

python 复制代码
import matplotlib.pyplot as plt

# 定义有限域上的曲线方程
def curve(x):
    return (x**3 + x + 1) % 23

# 生成离散的数据点
x = []
y = []
txt = []
for i in range(23):
    for j in range(23):
        if curve(i) == j**2 % 23:
            x.append(i)
            y.append(j)
            txt.append('(' +str(i)+',' + str(j)+')')
            
# 绘制曲线图像
plt.figure(figsize=(13, 10))
plt.scatter(x, y, marker='o' , s=50)

for i in range(len(x)):
    plt.annotate(txt[i], xy = (x[i], y[i]), xytext = (x[i]+0.05, y[i]+0.1),fontsize=15) 

plt.xlabel('x', fontsize=20)
plt.ylabel('y', fontsize=20)
plt.title('y^2 ≡ x^3 + x + 1 (mod 23)',fontsize=25)
plt.xlim(-1,15)
plt.ylim(-2,24)
plt.grid(True)
plt.show()

图上的每一点都满足 <math xmlns="http://www.w3.org/1998/Math/MathML"> y 2 ≡ x 3 + x + 1 ( m o d 23 ) y^2 \equiv x^3 + x +1 (mod 23) </math>y2≡x3+x+1(mod23)。 比如(3,10)这个点,恒等式左边 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 0 2 m o d ( 23 ) = 100 ( m o d 23 ) = 8 10^2 mod(23) = 100 (mod23) =8 </math>102mod(23)=100(mod23)=8, 恒等式右边 <math xmlns="http://www.w3.org/1998/Math/MathML"> 3 3 + 3 + 1 m o d ( 23 ) = 8 3^3 + 3 +1 mod(23) = 8 </math>33+3+1mod(23)=8.

接下来试试有限域上面的加法, 给定一点 <math xmlns="http://www.w3.org/1998/Math/MathML"> P = ( 3 , 10 ) P=(3,10) </math>P=(3,10), 求 <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 P 2P </math>2P

解:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> 2 P = P + P k ≡ ( 3 ∗ x 1 2 + a ) / 2 ∗ y 1 ( m o d 23 ) = ( 3 ∗ 3 2 + 1 ) / ( 2 ∗ 10 ) = 7 / 5 ( m o d 23 ) 令 n ≡ 7 / 5 ( m o d 23 ) → 5 ∗ n ≡ 7 m o d ( 23 ) = 7 → n = 6 故 k = 6 x 3 = 6 2 − 3 − 3 ( m o d 23 ) = 7 ( m o d 23 ) = 7 y 3 = 6 ∗ ( 3 − 7 ) − 10 = − 34 ( m o d 23 ) = 12 即 : 2 P = ( 7 , 12 ) 2P= P+P \newline k \equiv (3*x_1^2 +a) / 2*y_1 (mod 23) = (3*3^2 +1) / (2* 10) = 7/5 (mod23) \newline 令 n \equiv 7/5 (mod23) \rightarrow 5*n \equiv 7 mod(23)=7 \rightarrow n=6 \newline 故 k=6 \newline x_3 = 6^2-3-3 (mod 23) = 7 (mod23)= 7 \newline y_3 = 6*(3-7)-10 = -34 (mod 23) =12 \newline 即: 2P= (7,12) </math>2P=P+Pk≡(3∗x12+a)/2∗y1(mod23)=(3∗32+1)/(2∗10)=7/5(mod23)令n≡7/5(mod23)→5∗n≡7mod(23)=7→n=6故k=6x3=62−3−3(mod23)=7(mod23)=7y3=6∗(3−7)−10=−34(mod23)=12即:2P=(7,12)

依此类推, 可以计算 3P, 4P ..., kP

我们用python计算 P, 2P, ..., 22P

python 复制代码
import matplotlib.pyplot as plt

# 定义有限域GF(p)
p = 23 

# 定义椭圆曲线方程 y^2 = x^3 + ax + b (mod p)
a = 1
b = 1

# 定义椭圆曲线上一个点P(x, y)
P = (3, 10) 

# 点乘运算 Q = dP
def ecc_point_multiply(P, d, a, p):
    Q = (0, 0)
    for i in range(d):
        Q = ecc_point_add(Q, P, a, p)
    return Q

# 点加法
def ecc_point_add(P, Q, a, p):
    if P == (0, 0):
        return Q
    if Q == (0, 0):
        return P
    x1, y1 = P
    x2, y2 = Q
    if x1 == x2 and y1 != y2:
        return (0, 0)
    if P == Q:
        return ecc_point_double(P, a, p)

    s = (y2 - y1) * pow(x2 - x1, p - 2, p) % p
    x3 = (s*s - x1 - x2) % p 
    y3 = (s*(x1 - x3) - y1) % p
    return (x3, y3)

# 点倍运算  
def ecc_point_double(P, a, p):
    if P == (0, 0):
        return (0, 0)
    x1, y1 = P
    s = (3*x1*x1 + a) * pow(2*y1, p-2, p) % p
    x3 = (s*s - 2*x1) % p
    y3 = (s*(x1 - x3) - y1) % p
    return (x3, y3)
 

k=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]
Q=[]
txt = []
for i  in range(len(k)):
     Q.append ( ecc_point_multiply(P, k[i], a, p) )
     txt.append(str(i+1)+'P')
        
plt.figure(figsize=(15, 10))

for i  in range(len(Q)):
    plt.scatter(Q[i][0], Q[i][1], marker='o' , s=50, color ='gray')

for i  in range(len(Q)):
    plt.annotate(txt[i], xy = (Q[i][0], Q[i][1]), xytext = (Q[i][0]+0.1, Q[i][1]+0.1),fontsize=15) 
 
for i  in range(len(Q)-1):  
    plt.plot([Q[i][0],Q[i+1][0]] , [Q[i][1],Q[i+1][1]] )

plt.xlabel('x', fontsize=20)
plt.ylabel('y', fontsize=20)
plt.title('y^2 ≡ x^3 + x + 1 (mod 23), p=(3,10), Q=kP',fontsize=20)
plt.xlim(-1,20 )
plt.ylim(-1,23)
plt.grid(linewidth=0.5, color='gray')
plt.show()

说明一下这里计算 Q=kP 还是使用了暴力计算(和逆求解k相同),并没有使用 将 逐步二分拆解的优化(比如100P = 50P+50P, 而 50P =25P+ 25P , 25P =1P +24P, 24P = 12P +12P, 12P=6P+6P, 6P=3P+3P, 3P=1P+2P, 2P=1P+1P, 只需计算8次这种优化) 。这里的 k 和 p都 比较小, 放上代码只作为一个 demo。实际应用中还是得通过优化将为 logK。

可以看到在有限域上的乘法操作非常的无序,混乱,完全没有任何规律。这种杂乱无章的特性就非常适合用来加密, 让人无从下手,找不到任何规律,才能保障密文的安全性。

总结

至此,我已经完整地定性地推导了一次ECC椭圆曲线算法。

回顾一下, 我们从椭圆曲线的 几何加法说起, 从加法推广到乘法, 从乘法分析计算过程得出: 给定一条椭圆曲线 和基点 P, 正向计算 Q=kP 非常容易, 但反向计算 k 十分困难。 这种正向计算容易, 反向计算困难的性质,就是ECC椭圆曲线加密算法的保障。我们 顺势推出如何利用这一性质设置加密用的公钥和解密用的私钥, 并且做了安全性分析。接着我们从连续的椭圆曲线推广到离散且有界的椭圆曲线(有限域)。

相关推荐
嘉里蓝海2 小时前
我在嘉顺达蓝海的安全日常
安全
2301_780789663 小时前
渗透测试真的能发现系统漏洞吗
服务器·网络·安全·web安全·网络安全
嘉里蓝海3 小时前
我在嘉顺达蓝海的安全坚守
安全
你的人类朋友4 小时前
认识一下Bcrypt哈希算法
后端·安全·程序员
moz与京5 小时前
【面试向】热门技术话题(上)
人工智能·物联网·机器学习·面试·web3·区块链·元宇宙
Coovally AI模型快速验证9 小时前
基于YOLO集成模型的无人机多光谱风电部件缺陷检测
人工智能·安全·yolo·目标跟踪·无人机
夏天的风9910 小时前
本地部署PLM系统,如何用 ZeroNews 实现远程访问?
安全·远程工作
wanhengidc10 小时前
高性价比云手机挑选指南
运维·网络·安全·游戏·智能手机
拉法豆粉13 小时前
三方软件测试可移植性测试哪些内容
数据库·安全
熊文豪13 小时前
【华为OD】区块链文件转储系统
算法·华为od·区块链