crypto-ECC加密算法:从入门到实践

椭圆加密算法(ECC)是一种公钥加密体制,全称为“Elliptic Curve Cryptography”,最初Koblitz和Miller两人于1985年提出,是一种基于椭圆曲线数学的公开密钥加密算法。与传统的基于大质数分解难题的加密算法不同,该加密方式基于“离散对数”这种数学难题。
公钥密码体制根据其所依据的难题一般分为三类:大素数分解问题类、离散对数问题类、椭圆曲线类。有时也把椭圆曲线类归为离散对数类。相比RSA,ECC优势是可以使用更短的密钥,来实现与RSA相当或更高的安全,RSA加密算法也是一种非对称加密算法,在公开密钥加密和电子商业中RSA被广泛使用。常用于通讯加密,数字签名等。
据研究,160位ECC加密安全性相当于1024位RSA加密,210位ECC加密安全性相当于2048位RSA加密。bitcoin以及漂亮国gov都在用。相当好,但是由于后门问题等一些原因还未大量普及,目前我国居民二代身份证正在使用 256 位的椭圆曲线密码,虚拟货币比特币也选择ECC作为加密算法。

1、椭圆曲线

要想理解ECC,那么椭圆曲线一定是一个不能回避的问题。ECC加密过程是基于椭圆曲线进行的,但不用太过担心,我们不需要太过深入,只需大致了解一些基本知识,甚至很多细节也都不用深究。
说到椭圆,大家一定会先想到的是高中学到的椭圆方程:
x 2 a 2 + y 2 b 2 = 1 {x^2\over a^2}+{y^2\over b^2}=1 a2x2+b2y2=1
但其实椭圆曲线与这个高中时代的椭圆曲线方程基本无关。椭圆曲线的椭圆一词来源于椭圆周长积分公式。(有兴趣的可以去看一看)
##1-1定义
椭圆曲线是一条由方程 y 2 = x 3 + a x + b y^2=x^3+ax+b y2=x3+ax+b给定的曲线,说白了只要满足以上形式的方程都是椭圆曲线,其中a、b为常数,并满足 4 a 3 + 27 b 2 ! = 0 4a^3+27b^2!=0 4a3+27b2!=0(确保曲线上没有奇点,即曲线上任意一个点都有切线,确保每个点都有切线是十分重要的),椭圆曲线大致图像如下:
在这里插入图片描述

当参数a和b的值发生变化时,图像的形状和位置也会随之发生改变。然而,尽管图像的外观可能有所不同,椭圆曲线的核心特征始终保持不变。椭圆曲线的一个显著特点是它具有关于x轴对称的性质,这意味着无论a和b如何变化,曲线在x轴两侧的形状总是对称的。这个对称性在椭圆曲线的运算性质中扮演着至关重要的角色。(记住这个特点,在后面讲述椭圆曲线的运算性质的时候会用到)

1-2椭圆曲线的运算

1-2-1阿贝尔群

椭圆曲线上的点运算确实与我们日常所理解的实数运算有很大的不同。在椭圆曲线上,点之间并不是进行传统的加减乘除,而是基于一种特定的数学结构——群(Group)来进行运算。这里的“群”是一个抽象代数概念,与社交媒体上的“微信群”或“QQ群”是完全不同的。
19世纪,挪威数学家尼尔斯·阿贝尔(Niels Abel)对群的概念进行了抽象和深入研究,他提出的加群(也叫阿贝尔群或交换群)是数学中群的一种特例。在加群中,元素的加法运算满足交换律,即对于任意两个元素a和b,都有a+b=b+a。数学中的群通常用符号G来表示,其中的元素用加法来表示。
为了确保群上的运算具有意义且满足数学上的严谨性,这个“加法”必须遵循以下四个基本特性:

  • 封闭性(Closure):对于群G中的任意两个元素a和b,它们的和(即a+b)也必须是群G中的元素。这确保了群内的运算不会“溢出”到群外。

  • 结合性(Associativity):对于群G中的任意三个元素a、b和c,加法的结合律必须成立,即(a+b)+c=a+(b+c)。这保证了群上的运算不依赖于加法的顺序。

  • 单位元(Identity Element):群G中必须存在一个元素e(通常称为零元或单位元),使得对于群G中的任意元素a,都有e+a=a+e=a。这相当于实数中的0,任何数与0相加都等于它本身。

  • 逆元(Inverse Element):对于群G中的任意元素a,必须存在一个元素b(称为a的逆元),使得a+b=b+a=e(e是单位元)。这相当于实数中的负数,每个数都有一个与之相反的数,它们的和为零。
    在椭圆曲线密码学中,我们关注的是椭圆曲线上的点群,其中点的加法运算满足上述群的四个基本特性。这种特殊的加法运算允许我们在椭圆曲线上进行加密、解密和签名等操作,从而确保数据的安全性和完整性。
    当一个群在满足以上条件的同时,还满足交换律:a + b = b + a,那么就称这个群为阿贝尔群(或交换群),所以阿贝尔群是一个有点特殊的群。
    如果你现在开始感到有些困惑,不明白上面所讨论的群和椭圆曲线加法的概念,那么让我们来简化并重新解释一下。
    首先,群的概念在数学中是为了研究具有某种共同特性的对象集合以及这些对象之间的相互作用而建立的。在群的定义中,我们为集合中的元素定义了一个新的“加法”运算,但这个“加法”并不一定是我们通常意义上的加法。它只是一种抽象的运算规则,满足封闭性、结合性,并且存在单位元和逆元。
    为了帮助你更好地理解这个概念,我们可以举一个简单的例子,使用我们熟悉的实数进行模运算。模运算是一种在整数上进行的运算,其结果被限制在一个特定的范围内,即模数。例如,我们可以使用模7运算,这意味着所有的运算结果都将在0到6之间。
    现在,假设我们有两个数字1和2,我们要进行模7下的“加法”运算。根据模运算的规则,我们先进行普通的加法运算,然后将结果对7取模。所以:
    (1 + 2) mod 7 = 3 mod 7
    在这个例子中,“+”就是我们通常的加法运算,但由于我们引入了模7的概念,所以整个运算过程被限制在0到6的范围内。这就是一个简单的群的例子,其中实数集合在模7下形成了一个群,而“+”运算满足群的四个基本特性。
    接下来,当我们讨论椭圆曲线上的点时,我们也可以为这些点定义一个类似的“加法”运算。这个运算规则可能看起来与我们熟悉的加法不同,但它同样满足群的四个基本特性。通过这种方式,我们可以在椭圆曲线上进行加密、解密等操作,从而确保数据的安全性。
    希望这个例子能够帮助你更好地理解群的概念以及它在椭圆曲线加密中的应用。如果你还有其他疑问或需要进一步的解释,请随时告诉我。

    1-2-2椭圆曲线的加法

    在椭圆曲线上取两点A、B,画一条过A、B的直线,该直线与椭圆曲线交于一点,记该交点关于x轴对称位置的点为点C,定义为A+B,即为加法A + B = C。如下图所示:
    在这里插入图片描述

    简单点讲,找到A、B两点通过以上方式得到到点C,就是椭圆曲线这个群的“加法”,即A+B=C。
    此外,在椭圆曲线中只有“加法”,没有减法,所以我们只能写为+(-x)的形式,B=C+(-A),
    再用一次以上的“加法运算,”显然A与-A关于x轴对称,而A+(-A)通过下图显然是与椭圆曲线是没有交点的,所以A+(-A)=0,所以除了坐标系上曲线的点,椭圆曲线额外定义一个点(无穷远处),记为 0(因为群的封闭性)。

在这里插入图片描述

如果你考虑得比较得全面也许会问,如果所取的两个点重合怎么办?接下来就开始介绍椭圆曲线的二倍运算来为你解答疑惑。

1-2-3椭圆曲线的二倍运算

显然根据上述方法无法解释A + A,即两点重合的情况,因此在这种情况下,将椭圆曲线在A点的切线(前文提到要确保曲线没有奇点,原因就在这里),与椭圆曲线的交点,交点关于x轴对称位置的点,定义为A + A,即2A,即为二倍运算。
在这里插入图片描述

细心的朋友可能已经注意到,在椭圆曲线中,我们使用了“加法”来表示减法,比如A+(-A)=0,而这里的A+A=2A,这实际上就是乘法的基础。因此,在椭圆曲线的语境下,乘法是通过重复的加法来实现的。例如,3A的计算就可以理解为A与2A进行“加法”运算得到,这里的“加法”是椭圆曲线上定义的特殊加法。
进一步地,我们可以发现这种乘法的计算是可以拆分的。以5A为例,它可以被拆分为A+4A或2A+3A,这两种拆分方式在计算结果上是等价的。这种灵活性使得我们在处理椭圆曲线上的乘法运算时,可以根据实际情况选择最优的拆分策略,从而提高计算的效率。
总的来说,椭圆曲线上的乘法运算并不是我们常规理解的乘法,而是通过特殊的“加法”运算来实现的。这种特殊的加法运算满足群的性质,使得椭圆曲线在密码学中有着广泛的应用。

1-3有限域

椭圆曲线是连续的,容易被找到规律,并不适合用于加密;所以必须把椭圆曲线变成离散的点,要把椭圆曲线定义在有限域上。
实际上,域是一个可以在其上进行加法、减法、乘法和除法运算而结果不会超出域的集合。如有理数集合、实数集合、复数集合都是域,但整数集合不是(很明显,使用除法得到的分数或小数已超出整数集合)。
如果域F只包含有限个元素,则称其为有限域。有限域中元素的个数称为有限域的阶。尽管存在有无限个元素的无限域,但只有有限域在密码编码学中得到了广泛的应用。每个有限域的阶必为素数的幂,即有限域的阶可表示为pⁿ(p是素数、n是正整数),该有限域通常称为Galois域(Galois Fields),记为GF(pⁿ)。
在这里插入图片描述

1-3-1椭圆曲线有限域的阶

如果椭圆曲线上一点P,存在最小的正整数n使得数乘n P = O ∞ ,则将n称为P的阶若n不存在,则P是无限阶的.
在这里插入图片描述

计算可得27 P = − P = ( 3 , 13 ) 27P=-P=(3,13)27P=−P=(3,13) , 所以28 P = O 这些点做成了一个循环阿贝尔群,其中生成元为P,阶数为28。显然点的分布与顺序都是杂乱无章。简单来说就是,阶数为28时,P的倍数不能超过28,就像实数计算中的模一样。

2、ECC加密原理

2-1ECC的困难问题

首先,让我们深入了解加密算法的基本原理。加密算法之所以能够有效地保护数据的安全,其核心在于一个难以解决的数学问题。这个数学问题的难易程度直接决定了加密算法的加密强度。历史上,从凯撒密码的偏移位数未知性,到现代RSA算法中因数分解的复杂性,都是这些算法得以应用的基础。
当我们转向椭圆曲线加密算法(ECC)时,其核心数学难题可以这样描述:
假设P和Q是两个位于椭圆曲线上的点,而k是一个整数。根据ECC的定义,我们有以下关系:
Q=kP
在这里,点P被称为基点(base point),k是私钥(private key),而Q则是公钥(public key)。基于椭圆曲线的特定运算规则,当我们给定P和Q时,想要找出k的值是极其困难的。即使采用穷举法,在有限的计算资源下也几乎是不可能完成的任务。这种困难性正是ECC加密算法的核心所在,也是其提供高强度加密的原因。

2-2加密过程

1、选一条椭圆曲线Ep(a,b),并取椭圆曲线上一点作为基点P(这里的a、b是方程中x一次项系数和常数项)
2、选定一个大数k作为私钥,并生成公钥Q=kP,
3、加密:选择随机数r,将消息M生成密文C,密文是一个点对,即C=(rP, M+rQ)。
4、解密:M+rQ-k(rP)=M+r(kP)-k(rp)=M

2-3ECC保密通信过程

1.Alice选定一条椭圆曲线E,并取椭圆曲线上一点作为基点G 假设选定E29(4,20),基点G(13,23) , 基点G的阶数n=37
2.Alice选择一个私有密钥p(p<n),并生成公开密钥K=pG 比如25, K= pG = 25G = (14,6)
3.Alice将E和点K、G传给Bob
4.Bob收到信息后,将待传输的明文编码到上的一点M(编码方法略),并产生一个随机整数r(r<n,n为G的阶数) 假设r=6 要加密的信息为3,因为M也要在E29(4,20) 所以M=(3,28)
5.Bob计算点C1=M+rK和C2=rG C1= M+6K = (3,28)+6*(14,6)=(3,28)+(27,27)=(6,12) C2= 6G =(5,7)
6.Bob将C1、C2传给Alice
7.Alice收到信息后,计算C1-kC2,结果就应该是点M C1-kC2 =(6,12)-25C2 =(6,12)-25*6G =(6,12)-2G =(6,12)-(27,27) =(6,12)+(27,2) =(3,28)

3、性质

A(x1,y1),B(x2,y2)以及C(x3,y3)均在同一椭圆曲线y2=x3+ax+b上,且在有限域p上,其中C是AB直线与曲线的交点的关于x轴的对称点,即 A+B=C )有如下关系:
1、坐标的计算:
x3=k^2-x1-x2(modp)
y3=k(x1-x3)-y(modp)
2、斜率计算
若A=B,则k=(3x2+a)/2y1(A=B即要计算A点切线,需要求导)
若A≠B,则k=(y2-y1)/(x2-x1)
例:
在y2=x3+x+1(mod23)中,基点A(0,1),
若A=B,则k=(3*0^2+1)/2(mod23)=0.5mod23
稍微说一下这种小数的模运算:
n ≡ 0.5 m o d 23 n\equiv0.5mod23 n0.5mod23
2 n ≡ 1 m o d 23 ≡ 24 m o d 23 2n\equiv1mod23\equiv24mod23 2n1mod2324mod23
n ≡ 12 m o d 23 n\equiv12mod23 n12mod23
所以k=12,
x3=(12^2-0-0)mod23=144mod23=6,
y3=[12(0-6)-1]mod23=-73mod23=19
C(6,19)
此外对于n过大的数可以通过将其分解为2的多次幂的和,例如k=57时
57=25+24+23+20,所以:57P=25P+24P+23P+20P,以此来方便计算
下面用一道简单的ECC加密来实践一下
例题

from Crypto.Util.number import *
from gmpy2 import *
from random import *
from secret import flag

p=getPrime(600)
a=bytes_to_long(flag)
b=randrange(2,p-1)
E=EllipticCurve(GF(p),[a,b])
G=E.random_element()
 
x1,y1,_=G
G=2*G 
x2,y2,_=G
 
print(f"p = {p}")
print(f"b = {b}")
print(f"x1 = {x1}")
print(f"x2 = {x2}")

'''
p = 2978504498661002629091299071582436290465904673149953768927904567383228838983804727503168770704927560352042485566736762649409218645007506958368851729754839447148084715613471942096737
b = 203155971939366413053854572390533580854014345966175709774130058801484693709794880904842038444407635302410301870035839686400041986234396869946957773156780151597585571207840328547492
x1 = 2961746450301081157772024536903602376932457486781631924575753810982866784201238112170620297100938460251128698788082874030972104190128685218417680444326891343632886092532386859114448
x2 = 890136530671131957244004062733232817452520380339736536303621498985673475366221669385748542093619285319261702354866398531524362067791067563428973366865229056520602220404939306258451

'''

这是一道很简单的ECC,首先看椭圆曲线:
y2=x3+a*x+b
并且在本题中还有modp为条件,不妨令A(x1,y1),B(x2,y2),B=2A
然后再说明一下公式:
x 3 ≡ k 2 − x 1 − x 2 ( m o d p ) x3\equiv k^2-x1-x2(modp) x3k2x1x2(modp)
y 3 ≡ k ( x 1 − x 3 ) − y 1 ( m o d p ) y3\equiv k(x1-x3)-y1(modp) y3k(x1x3)y1(modp)
当两个点相同时,k=(3x1^2+a)/2y1(modp),所以
x2=k^2-2x1(modp)------------------------------1
k=(3x12+a)/(x3+ax+b)(modp)-------------2
由式1:
k^2=x2+2x1(modp)
将2式代入
x2+2x1=(3x12+a)2/(x3+ax+b)2(modp)
然后只要解方程就可以了

from Crypto.Util.number import *
from gmpy2 import *
p = 2978504498661002629091299071582436290465904673149953768927904567383228838983804727503168770704927560352042485566736762649409218645007506958368851729754839447148084715613471942096737
b = 203155971939366413053854572390533580854014345966175709774130058801484693709794880904842038444407635302410301870035839686400041986234396869946957773156780151597585571207840328547492
x1 = 2961746450301081157772024536903602376932457486781631924575753810982866784201238112170620297100938460251128698788082874030972104190128685218417680444326891343632886092532386859114448
x2 = 890136530671131957244004062733232817452520380339736536303621498985673475366221669385748542093619285319261702354866398531524362067791067563428973366865229056520602220404939306258451
k_2 = (x2 + 2 * x1)
PR.<a> = PolynomialRing(Zmod(p))
f = 4 * k_2 * (x1 ^ 3 + a * x1 + b) - (3 * x1 ^ 2 + a) ^ 2
a = f.roots()
for i in range(len(a)):
    m=a[i][0]
    flag = long_to_bytes(m)
    print(flag)
 b'flag{1s_1t_r3@lly_Easy?}'

[HGAME 2022 week4]ECC

from Crypto.Util.number import getPrime
from libnum import s2n
from secret import flag

p = getPrime(256)
a = getPrime(256)
b = getPrime(256)
E = EllipticCurve(GF(p),[a,b])
m = E.random_point()
G = E.random_point()
k = getPrime(256)
K = k * G
r = getPrime(256)
c1 = m + r * K
c2 = r * G
cipher_left = s2n(flag[:len(flag)//2]) * m[0]
cipher_right = s2n(flag[len(flag)//2:]) * m[1]

print(f"p = {p}")
print(f"a = {a}")
print(f"b = {b}")
print(f"k = {k}")
print(f"E = {E}")
print(f"c1 = {c1}")
print(f"c2 = {c2}")
print(f"cipher_left = {cipher_left}")
print(f"cipher_right = {cipher_right}")

'''
p = 74997021559434065975272431626618720725838473091721936616560359000648651891507
a = 61739043730332859978236469007948666997510544212362386629062032094925353519657
b = 87821782818477817609882526316479721490919815013668096771992360002467657827319
k = 93653874272176107584459982058527081604083871182797816204772644509623271061231
E = Elliptic Curve defined by y^2 = x^3 + 61739043730332859978236469007948666997510544212362386629062032094925353519657*x + 12824761259043751634610094689861000765081341921946160155432001001819005935812 over Finite Field of size 74997021559434065975272431626618720725838473091721936616560359000648651891507
c1 = (14455613666211899576018835165132438102011988264607146511938249744871964946084 : 25506582570581289714612640493258299813803157561796247330693768146763035791942 : 1)
c2 = (37554871162619456709183509122673929636457622251880199235054734523782483869931 : 71392055540616736539267960989304287083629288530398474590782366384873814477806 : 1)
cipher_left = 68208062402162616009217039034331142786282678107650228761709584478779998734710
cipher_right = 27453988545002384546706933590432585006240439443312571008791835203660152890619
'''

这是一道很适合刚学习ECC的人的一道题,首先看题:
K = k * G
c1 = m + r * K
c2 = r * G
G被称为基点,k是私钥,K而则是公钥
我们知道c1c2,这是我们之前讲到的基本加密过程中的几个重要数据,再用c1-k*c2就得到m,然后再用rsa的脚本解出来
脚本如下:

#sage
from Crypto.Util.number import *
p = 74997021559434065975272431626618720725838473091721936616560359000648651891507
a = 61739043730332859978236469007948666997510544212362386629062032094925353519657
b = 87821782818477817609882526316479721490919815013668096771992360002467657827319
k = 93653874272176107584459982058527081604083871182797816204772644509623271061231
cipher_left = 68208062402162616009217039034331142786282678107650228761709584478779998734710
cipher_right = 27453988545002384546706933590432585006240439443312571008791835203660152890619
E = EllipticCurve(GF(p),[a,b])#先E = EllipticCurve(GF(p),[a,b])建立一个椭圆曲线,要先建立后面才能用
c1 = E([14455613666211899576018835165132438102011988264607146511938249744871964946084 , 25506582570581289714612640493258299813803157561796247330693768146763035791942])
c2 = E( [37554871162619456709183509122673929636457622251880199235054734523782483869931 , 71392055540616736539267960989304287083629288530398474590782366384873814477806])

#这里要加上E说明是在E下上的点
m = c1 - k * c2
ml=cipher_left//m[0]
mr=cipher_right//m[1]
M=long_to_bytes(int(ml))+long_to_bytes(int(mr))#这里要加上int好像是计算出了的类型不是,不知道为什么
print(M)
#b'hgame{Ecc$is!sO@HaRd}'

这里我们是在sagemath中运行脚本,它能进行椭圆曲线中的加减乘除,是的,就是我们之前讲的特殊"加法"。在 SageMath 中,你可以轻松地进行椭圆曲线上的运算,包括加法、减法、乘法和点加倍等SageMath 提供了一个强大的数学框架,使得处理椭圆曲线密码学中的这些运算变得非常简单。下面再补充一些sagemath相关的内容,希望能帮到你。
补充
在sagemath中进行ECC加密的过程:首先,你需要定义一个椭圆曲线和一个基点。然后,你可以生成私钥和公钥,并进行加密和解密操作。下面是一个简单的示例,演示了如何在 SageMath 中进行这些操作:

# 导入必要的库
from sage.all import EllipticCurve, GF

# 定义椭圆曲线和基点
E = EllipticCurve(GF(p), [a, b])  # p 是一个素数,a 和 b 是椭圆曲线的参数
G = E(x0, y0)  # (x0, y0) 是椭圆曲线上的一个点,作为基点

# 生成私钥和公钥
k = GF(p).random_element()  # 随机生成私钥 k
K = k * G  # 计算公钥 K = k * G

# 加密过程
m = GF(p).random_element()  # 待加密的明文消息 m
r = GF(p).random_element()  # 随机数 r
c1 = m + r * K  # 加密后的密文 c1
c2 = r * G  # 加密后的密文 c2

# 解密过程
# 使用 c1 - k * c2 来恢复 m
m_decrypted = c1 - k * c2

# 输出结果
print("原始消息 m:", m)
print("加密后的密文 c1:", c1)
print("加密后的密文 c2:", c2)
print("解密后的消息 m_decrypted:", m_decrypted)

在这个脚本中,你需要替换 p、a、b、x0 和 y0 为你实际使用的椭圆曲线的参数和基点的坐标。GF§ 表示一个有限域,其中 p 是一个大素数。EllipticCurve 类用于定义椭圆曲线,而 G 是曲线上的一个点,用作加密的基点
(文章首发于先知社区)

  • 38
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值