橢圓曲線加密演算法,即:Elliptic Curve Cryptography,簡稱ECC,是基於橢圓曲線數學理論實現的一種非對稱加密演算法。相比RSA,ECC優勢是可以使用更短的金鑰,來實現與RSA相當或更高的安全。據研究,160位ECC加密安全性相當於1024位RSA加密,210位ECC加密安全性相當於2048位RSA加密。
橢圓曲線在密碼學中的使用,是1985年由Neal Koblitz和Victor Miller分別獨立提出的。
橢圓曲線一般情況下,橢圓曲線可用下列方程式來表示,其中a,b,c,d為係數。
例如,當a=1,b=0,c=-2,d=4時,所得到的橢圓曲線為:
該橢圓曲線E的影象如圖X-1所示,可以看出根本就不是橢圓形。
定義橢圓曲線的運算規則加法過曲線上的兩點A、B畫一條直線,找到直線與橢圓曲線的交點,交點關於x軸對稱位置的點,定義為A+B,即為加法。如下圖所示:A + B = C
二倍運算上述方法無法解釋A + A,即兩點重合的情況。因此在這種情況下,將橢圓曲線在A點的切線,與橢圓曲線的交點,交點關於x軸對稱位置的點,定義為A + A,即2A,即為二倍運算。
正負取反將A關於x軸對稱位置的點定義為-A,即橢圓曲線的正負取反運算。如下圖所示:
無窮遠點如果將A與-A相加,過A與-A的直線平行於y軸,可以認為直線與橢圓曲線相交於無窮遠點。
綜上,定義了A+B、2A運算,因此給定橢圓曲線的某一點G,可以求出2G、3G(即G + 2G)、4G......。即:當給定G點時,已知x,求xG點並不困難。反之,已知xG點,求x則非常困難。此即為橢圓曲線加密演算法背後的數學原理。
有限域上的橢圓曲線運算橢圓曲線要形成一條光滑的曲線,要求x,y取值均為實數,即實數域上的橢圓曲線。但橢圓曲線加密演算法,並非使用實數域,而是使用有限域。按數論定義,有限域GF(p)指給定某個質數p,由0、1、2......p-1共p個元素組成的整數集合中定義的加減乘除運算。
假設橢圓曲線為y² = x³ + x + 1,其在有限域GF(23)上時,寫作: y² ≡ x³ + x + 1 (mod 23)
此時,橢圓曲線不再是一條光滑曲線,而是一些不連續的點,如下圖所示。以點(1,7)為例,7² ≡ 1³ + 1 + 1 ≡ 3 (mod 23)。如此還有如下點:
(0,1) (0,22) (1,7) (1,16) (3,10) (3,13) (4,0) (5,4) (5,19) (6,4) (6,19) (7,11) (7,12) (9,7) (9,16) (11,3) (11,20) 等等。
另外,如果P(x,y)為橢圓曲線上的點,則-P即(x,-y)也為橢圓曲線上的點。如點P(0,1),-P=(0,-1)=(0,22)也為橢圓曲線上的點。
計算xG相關公式如下: 有限域GF(p)上的橢圓曲線y² = x³ + ax + b,若P(Xp, Yp), Q(Xq, Yq),且P≠-Q,則R(Xr,Yr) = P+Q 由如下規則確定:
Xr = (λ² - Xp - Xq) mod p Yr = (λ(Xp - Xr) - Yp) mod p 其中λ = (Yq - Yp)/(Xq - Xp) mod p(若P≠Q), λ = (3Xp² + a)/2Yp mod p(若P=Q)
因此,有限域GF(23)上的橢圓曲線y² ≡ x³ + x + 1 (mod 23),假設以(0,1)為G點,計算2G、3G、4G...xG等等,方法如下:
計算2G: λ = (3x0² + 1)/2x1 mod 23 = (1/2) mod 23 = 12 Xr = (12² - 0 - 0) mod 23 = 6 Yr = (12(0 - 6) - 1) mod 23 = 19 即2G為點(6,19)
計算3G: 3G = G + 2G,即(0,1) + (6,19) λ = (19 - 1)/(6 - 0) mod 23 = 3 Xr = (3² - 0 - 6) mod 23 = 3 Yr = (3(0 - 3) - 1) mod 23 = 13 即3G為點(3, 13)
橢圓曲線加解密演算法原理建立基於橢圓曲線的加密機制,需要找到類似RSA質因子分解或其他求離散對數這樣的難題。而橢圓曲線上的已知G和xG求x,是非常困難的,此即為橢圓曲線上的的離散對數問題。此處x即為私鑰,xG即為公鑰。
橢圓曲線加密演算法原理如下:
設私鑰、公鑰分別為k、K,即K = kG,其中G為G點。
公鑰加密: 選擇隨機數r,將訊息M生成密文C,該密文是一個點對,即: C = {rG, M+rK},其中K為公鑰
私鑰解密: M + rK - k(rG) = M + r(kG) - k(rG) = M 其中k、K分別為私鑰、公鑰。
橢圓曲線簽名演算法原理橢圓曲線簽名演算法,即ECDSA。 設私鑰、公鑰分別為k、K,即K = kG,其中G為G點。
私鑰簽名: 1、選擇隨機數r,計算點rG(x, y)。 2、根據隨機數r、訊息M的雜湊h、私鑰k,計算s = (h + kx)/r。 3、將訊息M、和簽名{rG, s}發給接收方。
公鑰驗證簽名: 1、接收方收到訊息M、以及簽名{rG=(x,y), s}。 2、根據訊息求雜湊h。 3、使用傳送方公鑰K計算:hG/s + xK/s,並與rG比較,如相等即驗籤成功。
原理如下: hG/s + xK/s = hG/s + x(kG)/s = (h+xk)G/s = r(h+xk)G / (h+kx) = rG
簽名過程假設要簽名的訊息是一個字串:“Hello World!”。DSA簽名的第一個步驟是對待簽名的訊息生成一個訊息摘要。不同的簽名演算法使用不同的訊息摘要演算法。而ECDSA256使用SHA256生成256位元的摘要。摘要生成結束後,應用簽名演算法對摘要進行簽名:產生一個隨機數k利用隨機數k,計算出兩個大數r和s。將r和s拼在一起就構成了對訊息摘要的簽名。這裡需要注意的是,因為隨機數k的存在,對於同一條訊息,使用同一個演算法,產生的簽名是不一樣的。從函式的角度來理解,簽名函式對同樣的輸入會產生不同的輸出。因為函式內部會將隨機值混入簽名的過程。
這個是網上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 -1def 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, y3def 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 = ypdef 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, y1def 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_ydef 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, yGdef 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 cdef 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)
由於本人水平有限,文章出現紕漏,還請大佬們斧正。