椭圆加密算法(ECC)是一种公钥加密体制,最初由Koblitz和Miller两人于1985年提出,其数学基础是利用椭圆曲线上的有理点构成Abel加法群上椭圆离散对数的计算困难性。公钥密码体制根据其所依据的难题一般分为三类:大素数分解问题类、离散对数问题类、椭圆曲线类。有时也把椭圆曲线类归为离散对数类。

之所以称其为椭圆曲线加密,是因为这种加密方式是在椭圆曲线方程上进行操作。即形如

swift ECC加密 ecc加密法_python

的方程。由于在标准的椭圆曲线方程上进行运算会出现小数或者无理数,从而导致精度的问题,所以我们通过取模,将椭圆曲线上的点变成整数点,并构成一个封闭群,便于后续运算。即变形成

swift ECC加密 ecc加密法_python_02

的形式,其中p一般取一个大素数。

椭圆曲线也可以有运算,像实数的加减乘除一样,这就需要使用到加群。19世纪挪威的尼尔斯·阿贝尔抽象出了加群(又叫阿贝尔群或交换群)。数学中的群是一个集合,我们为它定义了一个“加法”,并用符号+表示,遵循以下四个特性:

  • 封闭性:如果a和b都是封闭群的成员,那么a+b也是封闭群的成员;
  • 结合律:(a + b) + c = a + (b + c);
  • 单位元:a+0=0+a=a,0就是封闭群的单位元;
  • 逆元:对于任意值a必定存在b,使得a+b=0。

如果再增加一个条件,交换律:a + b = b + a,则称这个群为阿贝尔群,根据这个定义整数集是个阿贝尔群。

在椭圆曲线上,加法的运算定义如图1所示:过曲线上的两点A、B画一条直线,找到直线与椭圆曲线的交点,交点关于x轴对称位置的点,定义为A+B,即为加法。如下图所示:A + B = C

swift ECC加密 ecc加密法_swift ECC加密_03

 

图1 椭圆曲线加法运算

不妨举一个例子,假设椭圆曲线的方程为

swift ECC加密 ecc加密法_加密法_04

,设点A的坐标为(2,3),点B的坐标为(0,1),那么直线AB的方程为y=x+1,代入方程可知

swift ECC加密 ecc加密法_安全_05

,于是可很快求出第三个交点C的坐标(-1,0),也就是C=A+B。当然,这里C也满足C=B+A,所以椭圆曲线群是一个阿贝尔群。

swift ECC加密 ecc加密法_安全_06

 

图2 椭圆曲线加法运算

根据这个定义,我们来讨论一下一般情形下椭圆曲线的加法运算公式,假设椭圆曲线的方程为

swift ECC加密 ecc加密法_取模_07

,设A的坐标为(x1y1),B的坐标为(x2y2),那么直线AB的方程为

swift ECC加密 ecc加密法_取模_08

。带入椭圆曲线的方程,有

swift ECC加密 ecc加密法_python_09

,根据韦达定理,如果设直线AB与椭圆曲线的第三个交点为 (x3y3),那么就有

swift ECC加密 ecc加密法_python_10

,所以

swift ECC加密 ecc加密法_python_11

,由于最后还要将第三个交点的坐标关于x轴翻折一下,所以如果设C=A+B,那么C的坐标为:

swift ECC加密 ecc加密法_加密法_12

特别的,如果计算出来的C的坐标是一个分数,也就是形如

swift ECC加密 ecc加密法_安全_13

的形式。那么

swift ECC加密 ecc加密法_取模_14

的求法如下:由于

swift ECC加密 ecc加密法_取模_15

,设

swift ECC加密 ecc加密法_swift ECC加密_16

,那么1≡nb(modp)由费马小定理:如果p是一个质数,而整数a不是p的倍数,则有

swift ECC加密 ecc加密法_swift ECC加密_17

。带入上述式子可知,

swift ECC加密 ecc加密法_swift ECC加密_18

所以

swift ECC加密 ecc加密法_加密法_19

这边再举一个例子,设椭圆曲线方程为

swift ECC加密 ecc加密法_python_20

,A坐标为(4,4),B坐标为(0,1),那么根据公式

swift ECC加密 ecc加密法_python_21

,再根据上述分数取模的算法得到C=(33,14)。特别的,当A和B的坐标相等时,也就是A=B时,C=A+A,这个时候为了书写方便,可写为C=2A,乘法的计算就诞生了,如果要计算3A,4A或者更高的数nA时,只需要转化成加法,将n个A依次按照加法运算即可。这时候直线的斜率k就是该点的切线斜率,也就是

swift ECC加密 ecc加密法_安全_22

。举个例子:假设椭圆曲线的方程为y2x3+1(mod11),设点A的坐标是(2,3),那么k=2,切线方程为y=2x-1,带入公式求出2A的坐标(0,1)。

swift ECC加密 ecc加密法_python_23

 

图3 椭圆曲线乘法运算

椭圆曲线加密的原理就是利用C=nA的乘法运算时,知道n和A可以很快求出C,但是知道C和A很难求出n的值,下面来看一下椭圆曲线加密的具体流程。

首先定义如下概念:

基点P:指的是椭圆曲线上满足方程的任意一点,该点会公开。

私钥k:私钥k是一个大整数,该数由发送方和接收方私自保存,不公开。

公钥Q:公钥的计算法则是将私钥k和基点P相乘,做为Q,也就是Q=kP,公钥也会公开。

明文M:这里的明文M是一个点,假设我们要将一个数m进行加密,那么就需要计算出满足

swift ECC加密 ecc加密法_取模_24

的椭圆方程的一个y值构成点(m,y)。那么明文M的值就是(m,y),这个明文不公开。

加密法则E(M):加密法则如下,我们先随机选择一个随机数r,那么就可以得到密文c,c= E(M)=(rP,M+rQ),密文c是一个点对,密文c会公开。

解密法则D(c):解密法则如下,M=D(c)= M+rQ-krP。

举个例子:

假设椭圆曲线方程为

swift ECC加密 ecc加密法_加密法_25

,发送方和接收方选取的私钥k=7,基点P为(0,1),假设发送方想要把一个数4发送给接收方,那么先将x=4带入曲线方程,得到明文M(4,4)。再根据k和P计算出公钥Q=7P=(42, 14),随机选一个数r=3,那么密文c= E(M)=(rP,M+rQ)=((19, 28), (6, 45))。

接收方知道了密文c和私钥k,只需要按照解密法则M=D(c)= M+rQ-krP就可以求出明文M(4,4)。

python代码如下:


#coding:gbk  

p = 53  

i = lambda x: pow(x, p-2, p)  

def add(A, B):#加法运算  

    (u, v), (w, x) = A, B  

    assert u != w or v == x  

    if u == w: m = (3*u*w + 1) * i(v+x)  

    else: m = (x-v) * i(w-u)  

    y = m*m - u - w   

    z = m*(u-y) - v  

    return y % p, z % p  

def opposite(A):#取A的相反数,也就是将A变成-A  

    return A[0],(-A[1])%p  

def mul(t, A, B=0):#乘法运算  

    if not t:  

        return B  

    if t%2==0:  

        return mul(t//2, add(A,A), B)  

    elif B!=0:  

        return mul(t//2, add(A,A), add(B,A))  

    else:  

        return mul(t//2, add(A,A),A)  

M=(4,4)  

P=(0,1)  

r=3  

k=7  

Q=mul(k,P)  

c=(mul(r,P),add(M,mul(r,Q)))  

print("公钥Q是",Q)  

print("密文c是",c)  

print("明文M是",add(c[1],opposite(mul(k,c[0]))))


在CTF中,ECC加密是一种较为常见的题型,例如:攻防世界 (xctf.org.cn)

swift ECC加密 ecc加密法_安全_26

 

图4 题目描述

题目告诉了基点,私钥以及椭圆曲线的方程,让我们求公钥,python代码如下:


#coding:gbk  

p = 15424654874903  

a = 16546484  

i = lambda x: pow(x, p-2, p)  

def add(A, B):#加法运算  

    (u, v), (w, x) = A, B  

    assert u != w or v == x  

    if u == w: m = (3*u*w + a) * i(v+x)  

    else: m = (x-v) * i(w-u)  

    y = m*m - u - w   

    z = m*(u-y) - v  

    return y % p, z % p  

def opposite(A):#取A的相反数,也就是将A变成-A  

    return A[0],(-A[1])%p  

def mul(t, A, B=0):#乘法运算  

    if not t:  

        return B  

    if t%2==0:  

        return mul(t//2, add(A,A), B)  

    elif B!=0:  

        return mul(t//2, add(A,A), add(B,A))  

    else:  

        return mul(t//2, add(A,A),A)  

G=(6478678675,5636379357093)  

k=546768  

K=mul(k,G)  

print("公钥K是",K)#公钥K是 (13957031351290, 5520194834100)