椭圆曲线加密(以下简称ECC)实际上已经应用到了各个网站的HTTPS连接中。你平常访问的网站,大部分都是基于椭圆曲线加密,比如你现在正在浏览的CSDN。如果你用的是chrome浏览器,按下F12,点开Security,可以看到下图这样的内容:
这里的ECDHE就是椭圆曲线密钥交换的简称。能进行密钥交换的算法并非只有ECC,然而现在的大型网站(除了某些老旧的银行网站)都不约而同地选择了ECC,为啥?
还有大火的比特币,其身份认证机制便是以ECC为基础。先不论比特币的争议,其设计本身是十分精妙的。为何比特币选择ECC而不是RSA或者其它算法进行身份认证?
如果你是一个程序员或者运维人员,那么肯定没少用SSH连接服务器。为了在登陆时不输入密码,通常会使用公钥进行登录。这时会要求先在本机使用ssh-keygen
生成密钥对,然后把密钥对里的公钥上传到服务器中。但是你用多了有没有发现ssh-keygen
默认生成的密钥有点长?比如这个公钥:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCypa+az50x7bi0vweyY2dVQIztS9Q/v4DL3OQMPCPDR85bFsvsXWB5r/fbETDlo25ZDyWBInOVxqR96H0vKeWE28tbbQSqne41WAobPe1Z4gxq5o2WJXsC44qjW9ne34dJFVYNX9DrcnvddyZdTxw4Apa6A/hixMtaPDueQF6lct8EsVhkRqFSbdYfumABxUlGW4kKbwA86zT+jDCbnOHyk7EOvtUuLqlTntZmko7gm46QSuYNuhlFeGQirzmVmU8C55wABvVjeVw/wXZe96Q5faPEqAvY+X3o0ku1eliQuI/7BGq9j9s8q2WqSTBweOhJ5mHhf+kyra0jm70WYRlb
然而,你只需要把ssh-keygen
的密钥类型从默认的RSA切换到ECC,也就是运行ssh-keygen -t ed25519
,就可以得到一个短得多的公钥:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHzJZ8pHw7wVIFWp9zmLIeYyhk81QAp42FuCQkdbG1bb
一般来说,密钥越长安全性越高,但是这个短密钥的安全性比上面长的还要高。破解它的难度相当于破解长度为3000位(二进制位)的RSA密钥。而ssh-keygen
默认生成的是长度为2048位的RSA密钥。为什么ECC的密钥这么短却可以保证安全性?
不管是RSA、离散对数加密还是椭圆曲线加密,公钥加密算法都是依赖于某个正向计算很简单(比如多项式时间复杂度),而逆向计算很难(比如指数时间复杂度)的数学难题。对于RSA,这个问题是大整数因子分解问题;对于离散对数加密,是离散对数问题;对于椭圆曲线加密,则为椭圆曲线上的离散对数问题。
本文主要介绍椭圆曲线加密,但是离散对数加密和椭圆曲线加密原理比较相似,在这里一并介绍。
我们在中学里学的对数问题是指,
给定正实数 a a a和 a x a^x ax,求 x x x。也就是计算 x = log a a x x=\log_a{a^x} x=logaax。
这是实数域上的对数问题,不是什么难算的东西,随便按一下计算器结果就出来了。
而离散对数问题是指这样的问题:
给定素数 p p p和正整数 g g g,知道 g x m o d p g^x\mod p gxmodp的值,求 x x x
对于符合特定条件的 p p p和 g g g,这个问题是很难解的,更准确地说,是没有多项式时间的解法。而 g x m o d p g^x \mod p gxmodp的计算却非常快,由此造成了正向和逆向天差地别的计算速度。就像随手一扔,玻璃杯就摔碎成渣,而想要将一堆玻璃渣拼回完整的玻璃杯,即使做得到,所需的人力物力也远远大于当初那随手一扔。
Diffie–Hellman密钥交换(以下简称DH)是用于双方在可能被窃听环境下安全交换密钥的一种方法。
算法的安全性是由上面提到的离散对数难题保证。
具体算法流程如下:
- 小红和小明约定 p p p和 g g g的值
- 小红生成私钥 x x x,计算 g x m o d p g^x\mod p gxmodp作为公钥公布出去
- 小明生成私钥 y y y,计算 g y m o d p g^y\mod p gymodp作为公钥公布出去
- 小红得知 g y m o d p g^y\mod p gymodp后,计算
s = ( g y m o d p ) x m o d p = ( g y ) x m o d p = g x y m o d p s=(g^y\mod p)^x\mod p=(g^y)^x\mod p=g^{xy}\mod p s=(gymodp)xmodp=(gy)xmodp=gxymodp- 小明得到 g x m o d p g^x\mod p gxmodp后,计算
s = ( g x m o d p ) y m o d p = ( g x ) y m o d p = g x y m o d p s=(g^x\mod p)^y\mod p=(g^x)^y\mod p=g^{xy}\mod p s=(gxmodp)ymodp=(gx)ymodp=gxymodp- 双方都得到了相同的密钥的 s s s,交换完毕
上面的流程中, x x x和 y y y始终由两人自行保管的,第三方窃听得到的只有 p p p、 g g g、 g x m o d p g^x\mod p gxmodp和 g y m o d p g^y\mod p gymodp这几个值。
上面说过,离散对数是很难算的,所以第三方不能由这些信息计算出 x x x或 y y y,也就没办法计算出密钥 s s s了。
中学的时候我们学过圆锥曲线,比如椭圆、双曲线和抛物线。因为描述这些曲线的方程都是二次方程,圆锥曲线又被称为二次曲线。而椭圆曲线是则是由三次方程描述的一些曲线。更准确地说,椭圆曲线是由下面的方程描述的曲线:
y 2 = x 3 + a x + b 4 a 3 + 27 b 2 ≠ 0 y^2=x^3+ax+b\\ 4a^3+27b^2 \neq 0 y2=x3+ax+b4a3+27b2=0
需要注意的是,椭圆曲线之所以叫“椭圆曲线”,是因为其曲线方程跟利用微积分计算椭圆周长的公式相似。实际上它的图像跟椭圆完全不搭边。
下图是椭圆曲线 y 2 = x 3 − x + 1 y^2=x^3-x+1 y2=x3−x+1的图像
椭圆曲线有这样的两个性质:
- 关于X轴对称
- 画一条直线跟椭圆曲线相交,它们最多有三个交点
椭圆曲线加密之所以难破解,是因为其加密、解密运算是在椭圆曲线上进行的,所以接下来需要定义一些椭圆曲线上的运算。可以回想一下小学的时候第一次学整数加减法的情景,两者其实是类似的。
首先定义椭圆曲线上点的加法。设椭圆曲线上有两点,A和B点,那么作过这两点的直线与该曲线相交于第三点(C点),然后关于X轴对称得到D点,则D为这两个点的和,记作 D = A + B D=A+B D=A+B。很明显,D点也在该曲线上。所以椭圆曲线上两点之和也是曲线上的点。、
这个性质我们称之为封闭性,也就是只要A和B是曲线上的点,他们的和也必然是曲线上的点。类比于整数加法,只要相加的两个数是整数,那么他们的和也必然是整数。
特别地,如果两点重合,则作椭圆曲线在A点处的切线,与曲线相交于第二点(B点),然后关于X轴对称得到C点,则C点为A点与自身的和,记作 C = A + A C=A+A C=A+A
看到这里很多人可能会觉得疑惑,为什么要定义一个这么奇怪的加法?
实际上这个加法来源于椭圆曲线上利用已知有理点(横、纵坐标都是有理数的点)寻找其它有理点的方法,叫切线法(tangent and chord method)。这种加法可以保证以下两个结论是成立的:
这个时候聪明如你可能会发现,如果相加的两个点,A点和B点形成的直线恰好垂直于X轴,那么这条直线与椭圆曲线不管怎么算最多只有两个交点,上面的加法岂不是没法做了?为了补足这个缺陷,这里我们定义坐标系中距离X轴无穷远点为椭圆曲线上的一个特殊点,称为0点(零点)。 因为任意一条垂直于X轴的直线都会与椭圆曲线相交于0点。这里可能有点难以理解,实际上可以类比平面上平行线的定义。我们知道,两条直线必定有交点这一结论是错的,因为平行线是个例外。但是如果我们定义,两条平行的直线相交于无穷远点,那么这个结论就是成立的。
对于这个0点,有以下结论:
然后在加法的基础上,定义椭圆曲线上点的乘法。
设 P P P是椭圆曲线上的一个点,那么正整数 k k k乘以点 P P P的结果由下面的式子定义,注意式子中的加法是上面提到的椭圆曲线上点的加法:
1 ∗ P = P 1*P=P 1∗P=P
2 ∗ P = P + P 2*P=P+P 2∗P=P+P
3 ∗ P = 2 ∗ P + P 3*P=2*P+P 3∗P=2∗P+P
…
k ∗ P = ( k − 1 ) ∗ P + P k*P=(k-1)*P+P k∗P=(k−1)∗P+P
这个乘法满足以下性质:
对于任意正整数 k k k和 j j j,有
k ∗ ( j ∗ P ) = ( k j ) ∗ P = ( j k ) ∗ P = j ∗ ( k ∗ P ) k*(j*P)=(kj)*P=(jk)*P=j*(k*P) k∗(j∗P)=(kj)∗P=(jk)∗P=j∗(k∗P)
这个性质在椭圆曲线密钥交换中会利用到。
从程序实现的角度来考虑,假设有这么一个函数:
Point add(Point A, Point B) {...}
函数参数是两个椭圆曲线上的点,返回值是过两个点的直线与椭圆曲线相交的第三个点关于X轴对称的点。
那么按照如下方式调用函数:
Point result = P;
for (int i = 0; i < k - 1; i++)
result = add(P, result);
sendTo((result, P), others);
但是很显然,通过累加 k − 1 k-1 k−1次的方式计算 k ∗ P k*P k∗P是一种很笨的办法,其时间复杂度是线性的。上面我们提到过,椭圆曲线上点的加法是满足结合律的,即 ( A + B ) + C = A + ( B + C ) (A+B)+C=A+(B+C) (A+B)+C=A+(B+C),扩展一下,就有
P + P + P + P = ( P + P ) + ( P + P ) = 2 P + 2 P P+P+P+P=(P+P)+(P+P)=2P+2P P+P+P+P=(P+P)+(P+P)=2P+2P
于是就有这么一种骚操作,比如计算 16 P 16P 16P,我们可以先计算 2 P = P + P 2P=P+P 2P=P+P;然后计算 4 P = P + P + P + P = 2 P + 2 P 4P=P+P+P+P=2P+2P 4P=P+P+P+P=2P+2P;再计算 8 P = P + P . . . + P = 4 P + 4 P 8P=P+P...+P=4P+4P 8P=P+P...+P=4P+4P;最后计算 16 P = 8 P + 8 P 16P=8P+8P 16P=8P+8P。这里我们把15次加法减少到了4次。
当然,k的值不可能总是2的幂。实际上上面的操作可以推广到k为任意正整数的情况。比如计算23P,首先计算2P=P+P,然后
4 P = 2 P + 2 P 4P=2P+2P 4P=2P+2P
8 P = 4 P + 4 P 8P=4P+4P 8P=4P+4P
16 P = 8 P + 8 P 16P=8P+8P 16P=8P+8P
因为 23 = 16 + 4 + 2 + 1 23=16+4+2+1 23=16+4+2+1,所以 23 P = 16 P + 4 P + 2 P + P 23P=16P+4P+2P+P 23P=16P+4P+2P+P。总共只需要7次加法。
分析一下,对于任意正整数 k k k,我们都可以利用这个方法将计算 k ∗ P k*P k∗P所需的加法计算次数降低到 2 ⋅ ⌊ log 2 k ⌋ − 1 2\cdot \lfloor \log_2k\rfloor-1 2⋅⌊log2k⌋−1
也就是说,从时间复杂度的角度来看,这个算法是一个 O ( log k ) O(\log k) O(logk)的算法。
这个方法被称为快速幂算法,原本常用于快速计算某个数的k次幂,这里将其推广到椭圆曲线点乘的快速计算中。
为什么要在介绍了椭圆曲线上点的乘法后突然冒出一个快速幂算法?快速幂算法对于椭圆曲线加密有什么意义?因为数学家/密码学家发现,利用快速幂算法计算 k ∗ P k*P k∗P的时间复杂度是对数级的,但是要在知道 k ∗ P k*P k∗P和 P P P的前提下,倒推出 k k k的值,没有比挨个尝试 k k k的值快太多的算法。于是椭圆曲线加密依赖的数学难题就这么诞生了。
k k k为正整数, P P P是椭圆曲线上的点(称为基点),已知 k ∗ P k*P k∗P和 P P P,计算 k k k
如果我们改一种记法,把椭圆曲线上点的加法记作乘法,原来的乘法就变成了幂运算,那么上述难题的形式跟离散对数问题应该是一致的。即:
k k k为正整数, P P P是椭圆曲线上的点,已知 P k P^k Pk和 P P P,计算 k = log P P k k=\log_PP^k k=logPPk。
所以这个难题叫椭圆曲线上的离散对数问题。
尽管两者形式一致,但是他们并不等价。实际上这个问题比大整数质因子分解(RSA)和离散对数(DH)难题都要难得多,目前还没有出现亚指数级时间复杂度的算法(大整数质因子分解和离散对数问题都有),以致于同样的安全强度下,椭圆曲线加密的密钥比RSA和DH的短不少,这是椭圆曲线加密的一大优势。
但是密码学中,并不能使用上面介绍的实数域上的椭圆曲线。因为
所以我们需要引入有限域上的椭圆曲线。
所谓有限域上的椭圆曲线,简单来说就是满足下面式子要求的曲线(x, y, a, b都是小于素数p的非负整数):
y 2 = x 3 + a x + b m o d p 其 中 4 a 3 + 27 b 2 ≠ 0 m o d p y^2 =x^3+ax+b \mod p\\ 其中4a^3+27b^2\neq 0\mod p y2=x3+ax+bmodp其中4a3+27b2=0modp
对比一下原先的椭圆曲线的方程:
y 2 = x 3 + a x + b 其 中 4 a 3 + 27 b 2 ≠ 0 y^2=x^3+ax+b\\ 其中4a^3+27b^2\neq 0 y2=x3+ax+b其中4a3+27b2=0
可以看到这个只是对原式进行了简单的取模处理而已。实际上RSA和DH中也是基于这种形式的取模运算。
下图是椭圆曲线 y 2 = x 3 − x + 1 y^2 = x^3 - x + 1 y2=x3−x+1对素数97取模后的图像(图片来自参考文献)
原本连续光滑的曲线变成了离散的点,基本已经面目全非了,但是依然可以看到它是关于某条水平直线(y=97/2)对称的。
而且上面定义的椭圆曲线的加法仍然可用(当然乘法也可以)(图片来自参考文献)。
注意:密码学中有限域上的椭圆曲线一般有两种,一种是定义在以素数p为模的整数域 G F ( p ) GF(p) GF(p),也就是上面介绍的;另一种则是定义在特征为2的伽罗瓦域 G F ( 2 m ) GF(2^m) GF(2m)上,篇幅所限,这里就不介绍了。
ECDH跟DH的流程基本是一致的。
- 小红和小明约定使用某条椭圆曲线(包括曲线参数,有限域参数以及基点P等)
- 小红生成私钥 x x x,计算 x ∗ P x*P x∗P作为公钥公布出去
- 小明生成私钥 y y y,计算 y ∗ P y*P y∗P作为公钥公布出去
- 小红得知 y ∗ P y*P y∗P后,计算
s = x ∗ ( y ∗ P ) = x y ∗ P s=x*(y*P)=xy*P s=x∗(y∗P)=xy∗P- 小明得到 x ∗ P x*P x∗P后,计算
s = y ∗ ( x ∗ P ) = y x ∗ P s=y*(x*P)=yx*P s=y∗(x∗P)=yx∗P- 双方都得到了相同的密钥的 s s s,交换完毕
由于计算椭圆曲线上的离散对数是很难的,所以第三方没办法在只知道 x ∗ P x*P x∗P和 y ∗ P y*P y∗P的情况下计算出 x x x或 y y y的值。
实际应用中,我们并不需要关心椭圆曲线的众多参数如何选取(要选对参数对于普通使用者来说并不现实),只要从密码学家们精心挑选的一堆曲线中选择一个就行了。一般来说曲线Curve25519,prime256v1是比较常用的,比特币选择secp256k1是因为它效率较高,并且其参数是可预测的,降低了包含后门的可能性。
浅说椭圆曲线
A (relatively easy to understand) primer on elliptic curve cryptography
Wikipedia: Elliptic curve
Wikipedia: Elliptic curve cryptography
Bitcoin加密技术之椭圆曲线密码学
椭圆曲线密码体制