离散对数和椭圆曲线加密原理[通俗易懂]

离散对数和椭圆曲线加密原理[通俗易懂]序言现代公钥加密系统中,常用的加密算法除了RSA还有离散对数加密和椭圆曲线加密。这两者原理比较相似,在这里一并介绍。离散对数问题我们在中学里学的对数问题是指,给定正实数aaa和axaxa^x,求xxx。也就是计算x=logaaxx=loga⁡axx=\log_a{a^x}。这是实数域上的对数问题,不是什么难算的东西,随便按一下计算器结果就出来了。而离散对…

大家好,又见面了,我是你们的朋友全栈君。

为什么是椭圆曲线加密?

椭圆曲线加密(以下简称ECC)实际上已经应用到了各个网站的HTTPS连接中。你平常访问的网站,大部分都是基于椭圆曲线加密,比如你现在正在浏览的CSDN。如果你用的是chrome浏览器,按下F12,点开Security,可以看到下图这样的内容:
在这里插入图片描述
这里的ECDHE就是椭圆曲线密钥交换的简称。能进行密钥交换的算法并非只有ECC,但是现在的大型网站(除了某些老旧的银行网站)都不约而同地选择了ECC。还有大火的比特币,先不论比特币的争议,设计相当精妙,其身份认证机制便是以ECC为基础。为何比特币选择的也是ECC?

如果你是一个服务端程序员或者运维人员,那么肯定没少用SSH连接服务器。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密钥交换


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=x3x+1的图像
椭圆曲线

椭圆曲线有这样的两个性质:

  1. 关于X轴对称
  2. 画一条直线跟椭圆曲线相交,它们最多有三个交点

椭圆曲线上的运算


椭圆曲线加密之所以难破解,是因为其加密、解密运算是在椭圆曲线上进行的,所以接下来需要定义一些椭圆曲线上的运算。可以回想一下小学的时候第一次学整数加减法的情景,两者其实是类似的。

首先定义椭圆曲线上点的加法。设椭圆曲线上有两点,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
A+A
看到这里很多人可能会觉得疑惑,为什么要定义一个这么奇怪的加法?
实际上这个加法来源于椭圆曲线上利用已知有理点(横、纵坐标都是有理数的点)寻找其它有理点的方法,叫切线法(tangent and chord method)。这种加法可以保证以下两个结论是成立的:

  1. A + B = B + A A+B=B+A A+B=B+A
    交换律。这是显而易见的,直线没有方向,过A点作直线经过B点,和过B点作直线经过A点,得到的是同一条直线。
  2. ( A + B ) + C = A + ( B + C ) (A+B)+C=A+(B+C) (A+B)+C=A+(B+C)
    结合律。读者可以到GeoGebra自己试着画一下。这个结论的证明并不直观,详细的过程可以参考这里

这个时候聪明如你可能会发现,如果相加的两个点,A点和B点形成的直线恰好垂直于X轴,那么这条直线与椭圆曲线不管怎么算最多只有两个交点,上面的加法岂不是没法做了?为了补足这个缺陷,这里我们定义坐标系中距离X轴无穷远点为椭圆曲线上的一个特殊点,称为0点(零点)。 因为任意一条垂直于X轴的直线都会与椭圆曲线相交于0点。这里可能有点难以理解,实际上可以类比平面上平行线的定义。我们知道,两条直线必定有交点这一结论是错的,因为平行线是个例外。但是如果我们定义,两条平行的直线相交于无穷远点,那么这个结论就是成立的。

对于这个0点,有以下结论:

  1. 对于椭圆曲线上任意一点A,都存在曲线上另一点B,使得 A + B = 0 A+B=0 A+B=0
    因为椭圆曲线关于X轴对称,所以对于曲线上任意一点A,总存在另一点B使得过A、B的直线垂直于X轴,也就是该直线与曲线交于0点,所以A+B=0
  2. A + 0 = 0 + A = A A+0=0+A=A A+0=0+A=A
    因为0点是距离X轴无穷远的点,所以过A点与0点的直线是垂直于X轴的,它与曲线相交于另一点B点,那么B点关于X轴对称的点就是A点,即A点为A点和0点之和。
    A+0
    实际上这里“顺便”定义了椭圆曲线中的负数,若 A + B = 0 A+B=0 A+B=0,那么 B = − A B=-A B=A。椭圆曲线上点的减法也就自然而然地出现了, A − B = A + ( − B ) A-B=A+(-B) AB=A+(B)

就这样,我们得到了一个新的加法运算“体系”,类似整数加法,它有零,满足交换律、结合律,还有对应的减法。只是里面的基本元素从整数变成椭圆曲线上的点,加法的运算也变得略微有点奇怪。

我们对这个“体系”进一步拓展,在加法的基础上,定义椭圆曲线上点的乘法

P P P是椭圆曲线上的一个点,那么正整数 k k k乘以点 P P P的结果由下面的式子定义,注意式子中的加法是上面提到的椭圆曲线上点的加法:
1 ∗ P = P 1*P=P 1P=P
2 ∗ P = P + P 2*P=P+P 2P=P+P
3 ∗ P = 2 ∗ P + P 3*P=2*P+P 3P=2P+P

k ∗ P = ( k − 1 ) ∗ P + P k*P=(k-1)*P+P kP=(k1)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(jP)=(kj)P=(jk)P=j(kP)

这个性质在下文中的椭圆曲线密钥交换中会用到。

椭圆曲线上的离散对数问题


从程序实现的角度来考虑,假设有这么一个函数:

 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 k1次的方式计算 k ∗ P k*P kP是一种很笨的办法,其时间复杂度是线性的。上面我们提到过,椭圆曲线上点的加法是满足结合律的,即 ( 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 kP所需的加法计算次数降低到 2 ⋅ ⌊ log ⁡ 2 k ⌋ − 1 2\cdot \lfloor \log_2k\rfloor-1 2log2k1

也就是说,从时间复杂度的角度来看,这个算法是一个 O ( log ⁡ k ) O(\log k) O(logk)的算法。

这个方法被称为快速幂算法,原本常用于快速计算某个数的k次幂,这里将其推广到椭圆曲线点乘的快速计算中。

为什么要在介绍了椭圆曲线上点的乘法后突然冒出一个快速幂算法?快速幂算法对于椭圆曲线加密有什么意义?因为密码学家发现,利用快速幂算法计算 k ∗ P k*P kP的时间复杂度是对数级的,但是要在知道 k ∗ P k*P kP P P P的前提下,倒推出 k k k的值,没有比挨个尝试 k k k的值快太多的算法。于是椭圆曲线加密依赖的数学难题就这么诞生了。

k k k为正整数, P P P是椭圆曲线上的点(称为基点),已知 k ∗ P k*P kP 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的短不少的原因。

有限域上的椭圆曲线


但是密码学中,并不能直接用上面说的实数域上的椭圆曲线。因为

  1. 实数域上的椭圆曲线是连续的,有无限个点,密码学要求有限点。
  2. 实数域上的椭圆曲线的运算有误差,不精确。密码学要求精确。

所以我们需要引入有限域上的椭圆曲线。

所谓有限域上的椭圆曲线,简单来说就是满足下面式子要求的曲线(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+bmodp4a3+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+b4a3+27b2=0
可以看到这个只是对原式进行了简单的取模处理而已。实际上RSA和DH中也是基于这种形式的取模运算。

下图是椭圆曲线 y 2 = x 3 − x + 1 y^2 = x^3 – x + 1 y2=x3x+1对素数97取模后的图像(图片来自参考文献)
离散对数和椭圆曲线加密原理[通俗易懂]
原本连续光滑的曲线变成了离散的点,基本已经面目全非了,但是依然可以看到它是关于某条水平直线(y=97/2)对称的。

而且上面定义的椭圆曲线的加法仍然可用(当然乘法也可以)(图片来自参考文献)。
离散对数和椭圆曲线加密原理[通俗易懂]

注意:密码学中有限域上的椭圆曲线一般有两种,一种是定义在以素数p为模的整数域 G F ( p ) GF(p) GF(p),也就是上面介绍的;另一种则是定义在特征为2的伽罗瓦域 G F ( 2 m ) GF(2^m) GF(2m)上,篇幅所限,这里就不介绍了。

基于椭圆曲线的DH密钥交换(ECDH)


ECDH跟DH的流程基本是一致的。

  • 小红和小明约定使用某条椭圆曲线(包括曲线参数,有限域参数以及基点P等)
  • 小红生成私钥 x x x,计算 x ∗ P x*P xP作为公钥公布出去
  • 小明生成私钥 y y y,计算 y ∗ P y*P yP作为公钥公布出去
  • 小红得知 y ∗ P y*P yP后,计算
    s = x ∗ ( y ∗ P ) = x y ∗ P s=x*(y*P)=xy*P s=x(yP)=xyP
  • 小明得到 x ∗ P x*P xP后,计算
    s = y ∗ ( x ∗ P ) = y x ∗ P s=y*(x*P)=yx*P s=y(xP)=yxP
  • 双方都得到了相同的密钥的 s s s,交换完毕

由于计算椭圆曲线上的离散对数是很难的,所以第三方没办法在只知道 x ∗ P x*P xP y ∗ P y*P yP的情况下计算出 x x x y y y的值。

实际业务应用中,我们不用管椭圆曲线这一堆参数该怎么选(要选对参数对于普通用户来说并不现实),已经有一大批现成的曲线,集成在OpenSSL之类的基础库里,从里面选一个就行了。比如prime256v1是比较常用的曲线,很多常见网站的ECC加密算法都是用的它;而Curve25519是比较新的曲线,安全性和效率都很高,而且密钥更短,现在颇受青睐。比特币用的是secp256k1,它效率比较高,更重要的是它的参数是通过可预测(predictable)的方式选出来的,大大降低了包含后门的可能性。相比之下有的曲线虽然至今还没被发现有安全性问题,但是他们的参数选择却一直无法给出有效的解释,所以经常被怀疑藏有后门。

参考文献

浅说椭圆曲线
A (relatively easy to understand) primer on elliptic curve cryptography
Wikipedia: Elliptic curve
Wikipedia: Elliptic curve cryptography
Bitcoin加密技术之椭圆曲线密码学
椭圆曲线密码体制

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/139823.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • LINUX Shell常用命令

    LINUX Shell常用命令LinuxShell常用shell命令一、文件、目录操作命令1、ls命令功能:显示文件和目录的信息ls 以默认方式显示当前目录文件列表ls-a显示所有文件包括隐藏文件ls-l显示文件属性,包括大小,日期,符号连接,是否可读写及是否可执行ls-lh显示文件的大小,以容易理解的格式印出文件大小(例如1K234M2G)ls-lt显示文件,按照修改时…

  • 在windows cgywinportable上,通过运行linux命令,批量改动文件名。

    在windows cgywinportable上,通过运行linux命令,批量改动文件名。

  • pstack命令_压缩命令 linux

    pstack命令_压缩命令 linuxpstack命令可显示每个进程的栈跟踪。pstack命令必须由相应进程的属主或root运行。可以使用pstack来确定进程挂起的位置。此命令允许使用的唯一选项是要检查的进程的PID。pstree以树结构显示进程pstree-proot|grepphp-fpmroot为工作用户,-p为显示进程识别码,ps-Lf父进程号pstackPID号 转载…

  • Android 时间戳字符串转Date类型[通俗易懂]

    Android 时间戳字符串转Date类型[通俗易懂]Stringtimestamp=System.currentTimeMillis()+””;Datedate=newDate(Long.valueOf(timestamp));

  • Json详解以及fastjson使用教程[通俗易懂]

    Json是一种轻量级的数据交换格式,采用一种“键:值”对的文本格式来存储和表示数据,在系统交换数据过程中常常被使用,是一种理想的数据交换语言。在使用Java做Web开发时,不可避免的会遇到Json的使用。下面我们就简单讲一下Json的使用以及fastjson.jar包的使用。一:JSON形式与语法1.1:JSON对象我们先来看以下数据:{ "ID":1001, "name"…

  • 银行风控模型

    银行风控模型风控催生原因对于银行来说,现今互联网贷款和信用卡办理面临的主要难题是数据和风控。站在银行或金融机构角度,自然而然是想获得更多的信息和数据,但是在收集数据这方面又是比较无力的。加上当下的发展趋势,消费贷以及贷款审批速度都要求快。如何在快的的过程中对客户进行一个全面的审查,得出一个合理的结果呢?如果没有详细的数据对客户进行评估,这势必会提高放贷的风险。风控概述所谓风控,是指多银行贷款资金的…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号