淺析橢圓曲線加密算法(ECC)

本文首發於先知社區,原文連接:https://xz.aliyun.com/t/6295html

數學基礎

黎曼幾何中的「平行線」

歐幾里得《幾何本來》中提出五條公設:python

  1. 過相異兩點,能做且只能做一直線。
  2. 有限直線能夠任意地延長。
  3. 以任一點爲圓心、任意長爲半徑,可做一圓。
  4. 凡直角都相等。
  5. 兩直線被第三條直線所截,若是同側兩內角和小於兩個直角, 則兩直線做會在該側相交(平行公設)。

《幾何本來》中只有第29條命題,git

一條直線與兩條平行直線相交,則所成的內錯角相等,同位角相等,且同旁內角之和等於兩直角github

纔用到了第五公設,其餘命題並無使用到,所以一些數學家提出疑問:第五公設可否不做爲公設,而做爲一條定理?可否靠前四條公設證實之?所以出現了長期的關於「平行線理論」的討論。歐氏幾何講「過直線外一點有且只有一條直線與已知直線平行」,後面就有個羅氏幾何(羅巴切夫斯基)講「過直線外一點至少存在兩條直線和已知直線平行」,那麼是否有「過直線外一點,不能作直線和已知直線平行?」,黎曼幾何就回答了這個問題。web

黎曼幾何中不認可平行線的存在,即在同一平面內任何兩條直線都有公共點(交點)。另外一條公設講:直線能夠無限延長,但總的長度是有限的。算法

黎曼幾何也被稱爲橢圓幾何。橢圓曲線就是基於黎曼幾何的「平行線理論」。數組

定義平行線相較於無窮遠點P∞,使平面上全部直線都有惟一的交點。安全

clipboard.png

無窮遠點的性質:app

  • 一條直線只有一個無窮遠點。
  • 平面上一組相互平行的兩條直線有公共的無窮遠點。
  • 平面上任何相交的直線有不一樣的無窮遠點(不然就會存在兩個交點)。
  • 平面上全體無窮遠點構成一條無窮遠直線
  • 平面上全體無窮遠點與全體日常點構成射影平面

射影平面座標系

射影平面座標系是對笛卡爾平面直角座標系的擴展。普通平面直角座標系沒法表示無窮遠點,所以爲了表示無窮遠點的座標,產生了射影平面座標系。函數

射影平面點的表示:

對普通平面直角座標系上的點A(x,y),令x=X/Z,y=Y/Z(Z≠0),則點A在射影平面上表示爲(X:Y:Z)。

那麼對於平面直角作標記上的點(1,2),在射影平面上座標爲(Z:2Z:Z)Z≠0。

直線方程表示爲aX+bY+cZ=0。

兩條平行線L1: X+2Y+3Z=0與L2: X+2Y+Z=0,由於L1||L2,因此Z=0,X+2Y=0,因此無窮遠點座標爲(-2Y:Y:0)。

橢圓曲線方程

一條橢圓曲線在射影平面知足一齊次方程——威爾斯特拉斯方程:

的全部點的集合,且曲線上的每一個點都是非奇異(光滑)的。

橢圓曲線並非橢圓,是由橢圓曲線的描述方程相似於計算橢圓周長的方程而得名。

「非奇異」或「光滑」,能夠理解爲,知足方程的任意一點都存在切線。雖然有的方程知足上面的形式,可是並非橢圓曲線。

橢圓曲線上有一個無窮遠點(0:1:0)且知足方程。

如何把橢圓曲線放到平面直角座標系呢?射影平面座標系只多了個無窮遠點,所以求出平面直角座標系上橢圓曲線全部日常點組成的曲線方程,再加上無窮遠點,就構成了橢圓曲線。

把x=X/Z,y=Y/Z代入威爾斯特拉斯方程,獲得普通方程:

image.png

橢圓曲線上的羣操做

假設用加法符號「+」表示羣操做,給定兩個點及其座標,P(x1,y1),Q(x2,y2),計算第三個點R座標:

P+Q=R (x1,y1)+(x2,y2)=(x3,y3)

在橢圓曲線上定義阿貝爾羣,其運算法則:

任意選取橢圓曲線上兩點P、Q(若P、Q兩點重合,則做P點的切線)做直線交於橢圓曲線的另外一點R',過R'做y軸平行線交於R,規定P+Q=R。

相異點相加P+Q:

clipboard.png

相同點相加P+P:

clipboard.png

如有k個相同的P點相加,記做kP。

有限域上的橢圓曲線

實數域上的橢圓曲線是連續的,並不適用於加密,考慮到加密算法的可實現性,要把橢圓曲線定義在有限域上,使之變成離散的點。

給定一個有限域Fp:

  • Fp只有p(p爲素數)個元素0,1,2...p-2,p-1;
  • Fp的加法(a+b)法則是a+b≡c(mod p);
  • Fp的乘法(a×b)法則是a×b≡c(mod p);
  • Fp的除法(a÷b)法則是a÷b≡c(mod p),即a×b^-1≡c(mod p),b^-1爲b的逆元;
  • Fp的單位元是1,零元是0;
  • Fp域內運算知足交換律、結合律、分配律。

並不是全部的橢圓曲線都適合加密。下面定義一類適合加密的橢圓曲線:

橢圓曲線Ep(a,b),p爲質數,x,y∈[0,p-1]:

image.png

選擇兩個知足下列約束條件的小於p的非負整數a、b:

image.png

  • 無窮遠點O∞是零元,有O∞+O∞=O∞,O∞+P=P;
  • P(x,y)的負元是(x,-y),有P+(-P)=O∞;
  • P(x1,y1),Q(x2,y2)和R(x3,y3)有以下關係:
    image.png
    其中若P=Q則
    image.png
    ,若P≠Q則
    image.png
    k爲直線斜率。

橢圓曲線上點的階

若是橢圓曲線上一點P,存在最小的正整數n使得數乘nP=O∞,則稱n爲P的階,若n不存在,則P爲無限階。

在有限域上定義的橢圓曲線,全部點的階n都是存在的。

加密與解密

等式Q=dG(Q,G爲Ep(a,b)上的點,d爲小於n(n是點G的階)的整數),在有限域上的橢圓曲線,給定d和G,根據有限域上的加法法則,很容易計算出Q;但給定Q和G,很難計算出d。這就是橢圓曲線的離散對數難題。

點G稱爲基點,d爲私鑰,Q爲公鑰。

加解密步驟

  1. Alice選定一條橢圓曲線Ep(a,b),並取曲線上一點做爲基點G。
  2. Alice選擇一個d做爲私鑰,並生成公鑰Q=dG。
  3. Alice將曲線Ep(a,b)和點Q、G發給Bob。
  4. Bob收到信息後,將待傳輸的明文編碼到曲線Ep(a,b)上的一點M,並選擇一個隨機整數k(k<n)。
  5. Bob計算點C1=M+kQ;C2=kG。
  6. Bob將C1 C2發給Alice。
  7. Alice收到信息後,計算C1-dC2=M+kQ-d(kG)=M+k(dG)-d(kG)=M,獲得點M,再對點M進行解碼就獲得明文。

攻擊者從信道中截取信息,只能獲得Ep(a,b),Q,G,C1,C2,而經過Q、G求d或經過C一、C2求k都是困難的,所以攻擊者沒法獲取到明文。

密碼學中描述一條有限域上的橢圓曲線經常使用到六個參量:
T=(p,a,b,G,n,h)

p、a、b用來肯定一條橢圓曲線,G爲基點,n爲點G的階,h是有限域橢圓曲線上全部點的個數m與n相除獲得的整數部分。

這幾個參量取值的選擇,直接影響了加密的安全性。通常知足以下條件:

  1. p越大越好,但越大,計算速度會變慢,200位左右可知足通常安全需求;
  2. p≠n×h;
  3. image.png 1≤t<20;
  4. image.png
  5. n爲素數;
  6. h≤4。

Demo

這是參考網上的資料實現的簡易ECC加解密Demo:

# -*- coding:utf-8 -*-

def get_inverse(value, p):
    """
    求逆元
    :param value: 待求逆元的值
    :param p: 模數
    """
    for i in range(1, p):
        if (i * value) % p == 1:
            return i
    return -1

def get_gcd(value1, value2):
    """
    展轉相除法求最大公約數
    :param value1:
    :param value2:
    """
    if value2 == 0:
        return value1
    else:
        return get_gcd(value2, value1 % value2)

def get_PaddQ(x1, y1, x2, y2, a, p):
    """
    計算P+Q
    :param x1: P點橫座標
    :param y1: P點縱座標
    :param x2: Q點橫座標
    :param y2: Q點縱座標
    :param a: 曲線參數
    :param p: 曲線模數
    """
    flag = 1 # 定義符號位(+/-)

    # 若是P=Q,斜率k=(3x^2+a)/2y mod p
    if x1 == x2 and y1 == y2:
        member = 3 * (x1 ** 2) + a # 分子
        denominator = 2 * y1 # 分母

    # 若是P≠Q, 斜率k=(y2-y1)/(x2-x1) mod p
    else:
        member = y2 - y1
        denominator = x2 - x1

        if member * denominator < 0:
            flag = 0 # 表示負數
            member = abs(member)
            denominator = abs(denominator)

    # 化簡分子分母
    gcd = get_gcd(member, denominator) # 最大公約數
    member = member // gcd
    denominator = denominator // gcd
    # 求分母的逆元
    inverse_deno = get_inverse(denominator, p)
    # 求斜率
    k = (member * inverse_deno)
    if flag == 0:
        k = -k
    k = k % p

    # 計算P+Q=(x3,y3)
    x3 = (k ** 2 - x1 - x2) % p
    y3 = (k * (x1-x3) -y1) % p

    return x3, y3

def get_order(x0, y0, a, b, p):
    """
    計算橢圓曲線的階
    """
    x1 = x0 # -P的橫座標
    y1 = (-1 * y0) % p # -P的縱座標
    temp_x = x0
    temp_y = y0
    n = 1
    while True:
        n += 1
        # 累加P,獲得n*P=0∞
        xp, yp = get_PaddQ(temp_x, temp_y, x0, y0, a, p)
        # 若是(xp,yp)==-P,即(xp,yp)+P=0∞,此時n+1爲階數
        if xp == x1 and yp == y1:
            return n+1
        temp_x = xp
        temp_y = yp

def get_dot(x0, a, b, p):
    """
    計算P和-P
    """
    y0 = -1
    for i in range(p):
        # 知足適合加密的橢圓曲線條件,Ep(a,b),p爲質數,x,y∈[0,p-1]
        if i**2 % p == (x0**3 + a*x0 + b) % p:
            y0 = i
            break
    # 若是找不到合適的y0返回False
    if y0 == -1:
        return False
    # 計算-y
    x1 = x0
    y1 = (-1*y0) % p
    return x0, y0, x1, y1

def get_graph(a, b, p):
    """
    畫出橢圓曲線散點圖
    """
    xy = []
    # 初始化二維數組
    for i in range(p):
        xy.append(['-' for i in range(p)])

    for i in range(p):
        value = get_dot(i, a, b, p)
        if (value != False):
            x0,y0,x1,y1 = value
            xy[x0][y0] = 1
            xy[x1][y1] = 1

    print('橢圓曲線散點圖:')
    for i in range(p):
        temp = p - 1 -i
        if temp >= 10:
            print(temp, end='')
        else:
            print(temp, end='')

        # 輸出具體座標值
        for j in range(p):
            print(xy[j][temp], end='')
        print()

    print(' ', end='')
    for i in range(p):
        if i >= 10:
            print(i, end='')
        else:
            print(i, end='')

    print()

def get_nG(xG, yG, priv_key, a, p):
    """
    計算nG
    """
    temp_x = xG
    temp_y = yG
    while priv_key != 1:
        temp_x, temp_y = get_PaddQ(temp_x, temp_y, xG, yG, a, p)
        priv_key -= 1
    return temp_x, temp_y

def get_KEY():
    """
    生成公鑰私鑰
    """
    # 選擇曲線方程
    while True:
        a = int(input('輸入橢圓曲線參數a(a>0)的值:'))
        b = int(input('輸入橢圓曲線參數b(b>0)的值:'))
        p = int(input('輸入橢圓曲線參數p(p爲素數)的值:'))

        # 知足曲線判別式
        if (4*(a**3)+27*(b**2))%p == 0:
            print('輸入的參數有誤,請從新輸入!\n')
        else:
            break

    # 輸出曲線散點圖
    get_graph(a, b, p)

    # 選擇基點G
    print('在上圖座標系中選擇基點G的座標')
    xG = int(input('橫座標xG:'))
    yG = int(input('縱座標yG:'))

    # 獲取曲線的階
    n = get_order(xG, yG, a, b, p)

    # 生成私鑰key,且key<n
    priv_key = int(input('輸入私鑰key(<%d):'%n))
    #生成公鑰KEY
    xK, yK = get_nG(xG, yG, priv_key, a, p)
    return xK, yK, priv_key, a, b, p, n, xG, yG

def encrypt(xG, yG, xK, yK,priv_key, a, p, n):
    """
    加密
    """
    k = int(input('輸入一個整數k(<%d)用於計算kG和kQ:' % n))
    kGx, kGy = get_nG(xG, yG, priv_key, a, p) # kG
    kQx, kQy = get_nG(xK, yK, priv_key, a, p) # kQ
    plain = input('輸入須要加密的字符串:')
    plain = plain.strip()
    c = []
    print('密文爲:', end='')
    for char in plain:
        intchar = ord(char)
        cipher = intchar * kQx
        c.append([kGx, kGy, cipher])
        print('(%d,%d),%d' % (kGx, kGy, cipher), end=' ')

    print()
    return c

def decrypt(c, priv_key, a, p):
    """
    解密
    """
    for charArr in c:
        kQx, kQy = get_nG(charArr[0], charArr[1], priv_key, a, p)
        print(chr(charArr[2] // kQx), end='')
    print()


if __name__ == '__main__':
    xK, yK, priv_key, a, b, p, n, xG, yG = get_KEY()
    c = encrypt(xG, yG, xK, yK, priv_key, a, p, n)
    decrypt(c, priv_key, a, p)

注:加密函數中,計算密文是經過明文字符的ASCII碼乘點kQ的x座標獲得,即incharkQx;而一些加密中是將明文轉換爲大整數再轉換爲曲線上的點M(x,y),密文爲(C1=kG, C2=(M+kQ)),解密M=C1-priv_keyC2=M+kQ-priv_keykG=M+kpriv_keyG-priv_keykG

總結

本文初步介紹了ECC算法的基本原理和實現步驟,另外,橢圓曲線還應用於密鑰交換ECDH、數字簽名ECDSA等。一些區塊鏈項目中使用的加密算法也是橢圓曲線,如比特幣中的數字簽名算法Secp256k1。

因爲本人水平有限,文章出現紕漏,還請大佬們斧正。

參考文章

https://www.pediy.com/kssd/pediy06/pediy6014.htm

https://blog.dyboy.cn/websecurity/121.html

https://github.com/amintos/PyECC/tree/master/ecc

相關文章
相關標籤/搜索