本文首發於先知社區,原文連接:https://xz.aliyun.com/t/6295html
歐幾里得《幾何本來》中提出五條公設:python
《幾何本來》中只有第29條命題,git
一條直線與兩條平行直線相交,則所成的內錯角相等,同位角相等,且同旁內角之和等於兩直角github
纔用到了第五公設,其餘命題並無使用到,所以一些數學家提出疑問:第五公設可否不做爲公設,而做爲一條定理?可否靠前四條公設證實之?所以出現了長期的關於「平行線理論」的討論。歐氏幾何講「過直線外一點有且只有一條直線與已知直線平行」,後面就有個羅氏幾何(羅巴切夫斯基)講「過直線外一點至少存在兩條直線和已知直線平行」,那麼是否有「過直線外一點,不能作直線和已知直線平行?」,黎曼幾何就回答了這個問題。web
黎曼幾何中不認可平行線的存在,即在同一平面內任何兩條直線都有公共點(交點)。另外一條公設講:直線能夠無限延長,但總的長度是有限的。算法
黎曼幾何也被稱爲橢圓幾何。橢圓曲線就是基於黎曼幾何的「平行線理論」。數組
定義平行線相較於無窮遠點P∞,使平面上全部直線都有惟一的交點。安全
無窮遠點的性質: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代入威爾斯特拉斯方程,獲得普通方程:
假設用加法符號「+」表示羣操做,給定兩個點及其座標,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:
相同點相加P+P:
如有k個相同的P點相加,記做kP。
實數域上的橢圓曲線是連續的,並不適用於加密,考慮到加密算法的可實現性,要把橢圓曲線定義在有限域上,使之變成離散的點。
給定一個有限域Fp:
並不是全部的橢圓曲線都適合加密。下面定義一類適合加密的橢圓曲線:
橢圓曲線Ep(a,b),p爲質數,x,y∈[0,p-1]:
選擇兩個知足下列約束條件的小於p的非負整數a、b:
若是橢圓曲線上一點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爲公鑰。
攻擊者從信道中截取信息,只能獲得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相除獲得的整數部分。
這幾個參量取值的選擇,直接影響了加密的安全性。通常知足以下條件:
這是參考網上的資料實現的簡易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