1. 前言
在我們的印象中,密碼學的研究都是通過各種運算實現加密解密的,屬於代數里面的內容,而橢圓曲線屬於幾何學中的內容。兩者的結合可謂是十分的神奇了。
下面我們來學習下橢圓曲線的具體實現。
2. 什么是橢圓曲線
歐式幾何認為平行線不相交,而黎曼幾何認為任意兩條直線都相交於無窮遠點,也即平行線是不存在的。
橢圓曲線的英文為 Elliptic Curve,ECC對RSA產生了巨大的挑戰,ECC的主要優點是可以使用比RSA更短的密鑰獲取相同水平的安全性,這樣一來,加解密的時間就會縮短。
另外我們回顧下之前學習的非對稱加密算法,如Diffie-Hellman, ElGamal, Schnorr, DSA,這些都是建立在素數域GF(p)中的整數加法、乘法、冪和離散對數的運算性質上。我們可以遷移到ECC中來,所不同的是,運算的對象變為橢圓曲線上的點,比如在比特幣中使用的簽名算法就是ECDSA。
我們必須澄清的是,橢圓曲線的形狀,並不是真的是橢圓形的,只是因為橢圓曲線的描述方程,類似於計算橢圓周長的方程。我們使用的橢圓曲線(韋爾斯特拉斯(Weierstrass)方程)的表達式如下:y2+a1xy+a3y=x3+a2x^2+a4x+a6。注意,該方程中的一系列系數可以定義在有理數域,實數域,復數域甚至可以是有限域上。
我們看一個實數域上的橢圓曲線:
3. 橢圓曲線如何進行運算
我們將嘗試在橢圓曲線上進行各種運算的定義。
首先來回顧下,我們是如何定義實數域上的加法的。比如說為什么3+3=6,根據加法的定義,給定兩個整數的輸入,產生一個整數的輸出,同理,我們是否可以給定橢圓曲線上的加法,即給定兩個點A,B,得到另外一個點D呢?
加法:過曲線上的兩點A、B畫一條直線,找到直線與橢圓曲線的交點,交點關於x軸對稱位置的點,定義為A+B,即為加法。如下圖所示:A+B+C=0,即A+B=-C=D
如圖所示,根據前面的描述,我們得到下面的結論:
計算點A和點B的和的時候,首先將點A和點B相連形成一條直線,那么這條直線必然與該橢圓曲線相交於某點點C,再取點C沿X軸對稱的點便得到點D。
事情總不會這么順利,我們來考慮下面幾種特殊情況:值得注意的是,我們的討論是在黎曼幾何下面進行的,而不是在歐式幾何中,他們當中的區別在於,前者認為不存在平行的線,即任意兩條直線終將相交於無窮遠處。無窮遠點稱為零元。
二倍運算:上述方法無法解釋A + A,即兩點重合的情況。因此在這種情況下,將橢圓曲線在A點的切線,與橢圓曲線的交點,交點關於x軸對稱位置的點,定義為A + A,即2A,即為二倍運算。如下圖所示:A + A = 2A = B
正負取反:將A關於x軸對稱位置的點定義為-A,即橢圓曲線的正負取反運算。如下圖所示:
無窮遠點:如果將A與-A相加,過A與-A的直線平行於y軸,可以認為直線與橢圓曲線相交於無窮遠點。如下圖所示:
下面我們來推導下橢圓曲線上的一般加法公式:
下面對於一般的橢圓曲線方程,我們利用P, Q點的坐標(x1,y1),(x2,y2),給出求R=P+Q的坐標(x4,y4)的一般公式:
求橢圓曲線方程y2+a1xy+a3y=x3+a2x^2+a4x+a6上點P(x1,y1), Q(x2,y2)的和R(x4,y4)的坐標。
解: 當 Q=O時, P+Q=P。當Q≠O時:
(1) 先求點-R(x3,y3) 。
因為P,Q,-R三點共線,故設共線方程為y=kx+b,其中
若P≠Q(P,Q兩點不重合) 則直線斜率k=(y1-y2)/(x1-x2) ,當x1=x2,則P+Q=O;
若P=Q(P,Q兩點重合) 則直線為橢圓曲線的切線,其斜率為k=-Fx(x,y)/Fy(x,y)
= (3x^2+2a2x+a4-a1y)/(2y+a1x+a3),當2y+a1x+a3=0則P+Q=O 。
因此P,Q,-R三點的坐標值就是方程組:
y2+a1xy+a3y=x3+a2x^2+a4x+a6 -----------------[1]
y=(kx+b) -----------------[2]的解。
(2) 利用-R求R根據二次方程根與系數關系得:
y3+y4= -(a1x+a3)
故y4=-y3-(a1x+a3)=k(x1-x4)-y1-(a1x4+a3); 求出點R的縱坐標。
因為-R與R的連線平行於y軸,於是有x4=x3= k2+ka1-a2-x1-x2; --------
求出點R的橫坐標。
因為y3, y4為x=x4時方程y2+a1xy+a3y=x3+a2x2+a4x+a6的解,該等式化為一般方程y2+(a1x+a3)y-(x3+a2x2+a4x+a6)=0即P(x1, y1)+Q(x2, y2) = R(x4, y4) 的橫左邊與縱坐標分別為:
x4=k2+ka1-a2-x1-x2
y4=k(x1-x4)-y1-a1x4-a3
Δ 當P≠Q (P,Q兩點不重合) ,則直線斜率k=(y1-y2)/(x1-x2) ,若x1=x2,
則P+Q=O;
Δ 當P=Q (P,Q兩點重合) ,則直線為橢圓曲線的切線,其斜率為k=-
Fx(x,y)/Fy(x,y)= (3x2+2a2x+a4-a1y)/(2y+a1x+a3),若2y+a1x+a3=0則P+Q=O 。
綜上,定義了A+B、2A運算,因此給定橢圓曲線的某一點G,可以求出2G、3G(即G + 2G)、4G......。即:當給定G點時,已知x,求xG點並不困難。反之,已知xG點,求x則非常困難。此即為橢圓曲線加密算法背后的數學原理。
上面的討論全是在實數域上進行的,下面我們擴展到有限域上的橢圓曲線。
有限域上的運算:
- GF(p)的加法(a+b)是模p加法
- GF(p)的乘法(a×b)是模p乘法
- GF(p)的除法(a÷b)模p除法a乘以b模p的逆
橢圓曲線上的標量乘法
進一步,我們還可以定義在橢圓曲線上的標量乘法,如下所示:
A * 2 = A + A;
A * 3 = A + A + A;
...
如果將點和點之間的加法類比成整數之間的乘法,那么點的標量乘法不就相當於整數上的冪模運算嗎?反過來,點之間的除法運算不就相當於證書上的離散對數運算嗎?
4. GF(p)上的橢圓曲線
設p是大於3的素數,且4a3+27b2≠0(mod p),稱曲線y2=x3+ax+b(a,b∈GF(p))為GF(p)上的橢圓曲線。
由橢圓曲線方程可得到一同余方程:y2=x3+ax+b(mod p)(a,b∈GF(p))
其解為一個二元組(x,y),其中x,y∈GF(p),表示橢圓曲線上的一個點,稱為該橢圓曲線上的解點。
兩解點相加
設P(x1,y1)和Q(x2,y2)是解點,R(x3,y3)=P(x1,y1)+Q(x2,y2):
1.若P為無窮點,即P=O,此時R=P+Q=Q;若Q為無窮點,即Q=O,此時R=P+Q=P;若P和Q都為無窮點,即P=Q=O,則R=P+Q=O。
2.若x1=x2且y1=y2,即P=Q,此時R=P+Q=2P,其中
3.若x1=x2而y1=-y2,此時稱Q點為P點的逆,記為P=-Q,且R=P+Q=O。
4.除上述特殊情況之外的一般情況,即P≠±Q時,R=P+Q,其中
集合E={所有的解點,無窮點O}和加法運算構成加法交換群。設G(G≠O,即G為一個解點)為一個加法群的生成元,則使得nG=G+G+...+G=O的倍數n為該加法群的階。加法群的階整除集合E的階,即n | |E|。
求橢圓曲線的所有解點
當p較小,即GF(p)較小時,可以利用窮舉的方法根據同余方程y2=x3+ax+b(mod p)(a,b∈GF(p))求出所有解點。
具體方法為:求出x取0~p-1,x3+ax+b(mod p)的結果是否為模p的二次剩余。如果是,則一個x值可得到兩個對應的y值,也就得到互逆的兩個解點。
e.m.取p=11,橢圓曲線y2=x3+x+6
由此表得到所有的解點:(2,4)、(2,7)、(3,5)、(3,6)、(5,2)、(5,9)、(7,2)、(7,9)、(8,3)、(8,8)、(10,2)、(10,9),再加上無窮點O共13個點的集合E加上加法運算就構成一個加法交換群。
因為集合E的階|E|=13為素數,所以該加法群的階為13。
取G=(2,7)為生成元,G=(2,7),2G=(5,2),3G=(8,3),4G=(10,2),5G=(3,6),6G=(7,9),7G=(7,2),8G=(3,5),9G=(10,9),10G=(8,8),11G=(5,9),12G=(2,4),最終得到13G=O,所以加法群的階為13。
根據橢圓曲線的一般加法公式,對於GF(pr)(p>3)上的橢圓曲線方程y2=x^3+ax+b,給出Ep(a,b)上的加法公式:
設P,Q∈Ep(a,b),則:
- P+O=P;
- 如果P=(x,y),那么(x,y)+(x,-y)=O,即(x,-y)是P的加法逆元,表示為-P。
- 設P=(x1,y1), Q=(x2,y2), P≠-Q,則P+Q=(x4,y4),可由以下規則確定 x4=k^2+-x1-x2 modp,y =k(x -x )-y modp
其中,
5. 橢圓曲線上Diffie-Hellman密鑰交換
首先取一素數p≈2180,以及參數a, b,則橢圓曲線上的點構成Abel群Ep(a, b)。
取Ep(a, b)上的一個生成元G(x1, y1),要求G的階是一個非常大的數,G的階是滿足nG=O的最小正整數。
Ep(a, b)和G作為公鑰體制的公開密鑰參數,對外公布。
A選擇一小於n的整數nA作為私鑰,由PA=nAG產生Ep(a,b) 上的一點作為公鑰。
B類似地選取自己的私鑰nB,並計算自己的公鑰PB=nBG。
A可以獲得B的公鑰PB;
B可以獲得A的公鑰PA ;
A計算: K=nA× PB= nAnBG ;
B計算: K=nB× PA= nAnBG 。
至此,A和B共同擁有密鑰K= nAnBG。攻擊者如果想獲得密鑰K,他就必須由PA和G求出nA,或者由PB和G
求出nB,而這等價於求橢圓曲線上的離散對數問題ECDLP,因此是不可行的。
舉例:
選擇p=211,E211(0,-4),即橢圓曲線為y2≡x3-4mod 211;
G=(2,2)是E211(0,-4)上的一個生成元,階n=241,241G=O;
A取私鑰為nA=121,可計算公鑰PA=121 ×(2,2)=(115,48);
B取私鑰為nB=203,可計算公鑰PB=203 ×(2,2)=(130,203);
A計算共享密鑰:121×PB=121× (130,203)=(161,169);
B計算共享密鑰:203×PA=203× (115, 48)=(161,169);
可見,此時A和B共享密鑰是一對數據。如果在后續采用單鑰體制加密時,可以簡單地取其中的一個坐標,比如x坐標,或x坐標的一個簡單函數作為共享的密鑰進行加密/解密運算。
6. ELGamal型橢圓曲線密碼
1.選擇一個素數p,從而確定有限域GF(p),將p公開。
2.選擇元素a,b∈GF(p),從而確定一條GF(p)上的橢圓曲線,確定加法交換群E,將a和b公開。
3.選擇一個大素數n,並確定一個階為n的基點G(x,y),將n和G(x,y)公開。
4.余因子h=|E|/n,將h公開。
5.隨機選擇一個整數d(0<d<n)作為私鑰保密。
6.定義Q=dG作為公鑰公開。
加密
待發送消息Pm,任意用戶B隨機選擇臨時秘密參數k∈Zn,按照下列運算計算密文(C1, C2) :
C1=kG
C2=Pm+kPA
解密
為了解密(C1, C2),Alice計算 Pm=C2-nAC1
解密可行性:Pm=C2-nAC1=C2-nAkP=Pm+knAP-nAkP
例子
取公開參數p=751, Ep(-1,188),即橢圓曲線為y2=x3- x+188。選取基點為G=(0, 376),A的公鑰為PA=(201, 5)。
假設消息m嵌入到橢圓曲線上的點為Pm=(562, 201)。 加密者B選取隨機數k=386,計算密文
C1=kG=386(0, 376)=(676, 558),
C2=Pm+kPA =(562, 201)+386(201, 5)=(385, 328)。
推薦橢圓曲線
NIST向社會推薦了5條素域GF(p)上隨機選取的橢圓曲線:
P-192
p=2192-264-1
a=-3
b=64210519 E59C80E7 0FA7E9AB 72243049 FEB8DEEC C146B9B1
x=188DA80E B03090F6 7CBf20EB 43A18800 F4FF0AFD 82FF1012
y=07192B95 FFC8DA78 631011ED 6B24CDD5 73F977A1 1E794811
n=FFFFFFFF FFFFFFFF FFFFFFFF 99DEF836 146BC9B1 B4D22831
h=1
P-224
p=2224-296-1
a=-3
b=B4050A85 0C04B3AB F5413256 5044B0B7 D7BFD8BA 270B3943 2355FFB4
x=B70E0CBD 6BB4BF7F 321390B9 4A03C1D3 56C21122 343280D6 115C1D21
y=BD376388 B5F723FB 4C22DFE6 CD4375A0 5A074764 44D58199 85007E34
n=FFFFFFFF FFFFFFFF FFFFFFFF FFFF16A2 E0B8F03E 13DD2945 5C5C2A3D
h=1
P-256
p=2256-2224+2192+296-1
a=-3
b=5AC635D8 AA3A93E7 B3EBBD55 769886BC 651D06B0 CC53B0F6 3BCE3C3E 27D2604B
x=6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0 F4A13945 D898C296
y=4FE342E2 FE1A7F9B 8EE7EB4A 7C0F9E16 2BCE3357 6B315ECE CBB64068 37BF51F5
n=FFFFFFFF 00000000 FFFFFFFF FFFFFFFF BCE6FAAD A7179E84 F3B9CAC2 FC632551
h=1
P-384
p=2384-2128-296+232-1
a=-3
b=B3312FA7 E23EE7E4 988E056B E3F82D19 181D9C6E FE814112 0314088F 5013875A C656398D 8A2ED19D 2A85C8ED D3EC2AEF
x=AA87CA22 BE8B0537 8EB1C71E F320AD74 6E1D3B62 8BA79B98 59F741E0 82542A38 5502F25D BF55296C 3A545E38 72760AB7
y=3617DE4A 96262C6F 5D9E98BF 9292DC29 F8F41DBD 289A147C E9DA3113 B5F0B8C0 0A60B1CE 1D7E819D 7A431D7C 90EA0E5F
n=FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF C7634D81 F4372DDF 581A0DB2 48B0A77A ECEC196A CCC52973
h=1
P-521
p=2521-1
a=-3
b=00000051 953EB961 8E1C9A1F 929A21A0 B68540EE A2DA725B 99B315F3 B8B48991 8EF109E1 56193951 EC7E937B 1652C0BD 3BB1BF07 3573DF88 3D2C34F1 EF451FD4 6B503F00
x=000000C6 858E06B7 0404E9CD 9E3ECB66 2395B442 9C648139 053FB521 F828AF60 6B4D3DBA A14B5E77 EFE75928 FE1DC127 A2FFA8DE 3348B3C1 856A429B F97E7E31 C2E5BD66
y=00000118 39296A78 9A3BC004 5C8A5FB4 2C7D1BD9 98F54449 579B4468 17AFBD17 273E662C 97EE7299 5EF42640 C550B901 3FAD0761 353C7086 A272C240 88BE9476 9FD16650
n=000001FF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 51868783 BF2F966B 7FCC0148 F709A5D0 3BB5C9B8 899C47AE BB6FB71E 91386409
h=1
7. 基於Java的橢圓曲線密碼算法的實現
ECPoint:
import java.math.BigInteger;
public class ECPoint {
BigInteger x;
BigInteger y;
public ECPoint() {
x = null;
y = null;
}
public ECPoint(BigInteger x, BigInteger y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
if (isO())
return "O";
return "(" + x.toString(16) + ", " + y.toString(16) + ")";
}
boolean isO() {
if (x == null && y == null)
return true;
return false;
}
}
ECC_p.java
import java.math.BigInteger;
import java.util.Random;
public class ECC_p {
public static BigInteger p, a, b, x, y, n, h;
static EC_p ec;
public static ECPoint G, Q;
private static BigInteger d;
public ECC_p() {
int k = new Random().nextInt(5);
switch (k) {
case 0 :
init(192);
break;
case 1 :
init(244);
break;
case 2 :
init(256);
break;
case 3 :
init(384);
break;
case 4 :
init(521);
break;
}
}
public ECC_p(int k) {
init(k);
}
static void init(int k) {
switch (k) {
case 192 :
p = new BigInteger("2").pow(192).subtract(new BigInteger("2").pow(64)).subtract(new BigInteger("1"));
a = new BigInteger("-3");
b = new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16);
x = new BigInteger("188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012", 16);
y = new BigInteger("07192b95ffc8da78631011ed6b24cdd573f977a11e794811", 16);
n = new BigInteger("ffffffffffffffffffffffff99def836146bc9b1b4d22831", 16);
h = new BigInteger("1");
break;
case 224 :
p = new BigInteger("2").pow(224).subtract(new BigInteger("2").pow(96)).subtract(new BigInteger("1"));
a = new BigInteger("-3");
b = new BigInteger("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", 16);
x = new BigInteger("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", 16);
y = new BigInteger("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16);
n = new BigInteger("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d", 16);
h = new BigInteger("1");
break;
case 256 :
p = new BigInteger("2").pow(256).subtract(new BigInteger("2").pow(224)).add(new BigInteger("2").pow(192)).add(new BigInteger("2").pow(96)).subtract(new BigInteger("1"));
a = new BigInteger("-3");
b = new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16);
x = new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16);
y = new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16);
n = new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16);
h = new BigInteger("1");
break;
case 384 :
p = new BigInteger("2").pow(384).subtract(new BigInteger("2").pow(128)).subtract(new BigInteger("2").pow(96)).add(new BigInteger("2").pow(32)).subtract(new BigInteger("1"));
a = new BigInteger("-3");
b = new BigInteger("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", 16);
x = new BigInteger("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", 16);
y = new BigInteger("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", 16);
n = new BigInteger("ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973", 16);
h = new BigInteger("1");
break;
case 521 :
p = new BigInteger("2").pow(521).subtract(new BigInteger("1"));
a = new BigInteger("-3");
b = new BigInteger("00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00", 16);
x = new BigInteger("000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 16);
y = new BigInteger("0000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", 16);
n = new BigInteger("000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409", 16);
h = new BigInteger("1");
break;
}
ec = new EC_p(p, a, b);
G = new ECPoint(x, y);
d = new BigInteger(n.bitLength(), new Random());
Q = ec.multiply(G, d);
}
/**
* 加密
* @param M
* @return
*/
BigInteger[] encrypt(BigInteger M) {
BigInteger k;
ECPoint X1, X2;
do {
k = new BigInteger(n.bitLength(), new Random());
} while ((X2 = ec.multiply(Q, k)).x == null);
X1 = ec.multiply(G, k);
BigInteger[] C = new BigInteger[3];
C[0] = X1.x;
C[1] = X1.y;
C[2] = M.multiply(X2.x).mod(n);
return C;
}
/**
* 加密
* @param M
* @param k
* @return
*/
BigInteger[] encrypt(BigInteger M, BigInteger k) {
ECPoint X1 = ec.multiply(G, k);
ECPoint X2 = ec.multiply(Q, k);
BigInteger[] C = new BigInteger[3];
C[0] = X1.x;
C[1] = X1.y;
C[2] = M.multiply(X2.x).mod(n);
return C;
}
/**
* 解密
* @param C
* @return
*/
BigInteger decrypt(BigInteger[] C) {
ECPoint X1 = new ECPoint(C[0], C[1]);
ECPoint X2 = ec.multiply(X1, d);
BigInteger M = C[2].multiply(X2.x.modPow(new BigInteger("-1"), n)).mod(n);
return M;
}
}
EC_p.java
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
public class EC_p {
BigInteger p;
BigInteger a;
BigInteger b;
public EC_p(BigInteger p, BigInteger a, BigInteger b) {
this.p = p;
this.a = a;
this.b = b;
}
@Override
public String toString() {
return "y²=x³+" + a.toString(16) +"x+" + b.toString(16) + "(mod " + p.toString(16) + ")";
}
/**
* 兩解點相加
* @param p1
* @param p2
* @return
*/
ECPoint add(ECPoint p1, ECPoint p2) {
if (p1.isO()) return p2;
if (p2.isO()) return p1;
ECPoint p3 = new ECPoint();
BigInteger lambda;
if (p1.x.compareTo(p2.x) == 0) {
if (p1.y.compareTo(p2.y) == 0) {
lambda = new BigInteger("3").multiply(p1.x.pow(2)).add(a).multiply(new BigInteger("2").multiply(p1.y).modPow(new BigInteger("-1"), p)).mod(p);
p3.x = lambda.pow(2).subtract(new BigInteger("2").multiply(p1.x)).mod(p);
p3.y = lambda.multiply(p1.x.subtract(p3.x)).subtract(p1.y).mod(p);
return p3;
}
if (p1.y.compareTo(p.subtract(p2.y)) == 0)
return p3;
}
lambda = p2.y.subtract(p1.y).multiply(p2.x.subtract(p1.x).modPow(new BigInteger("-1"), p)).mod(p);
p3.x = lambda.pow(2).subtract(p1.x).subtract(p2.x).mod(p);
p3.y = lambda.multiply(p1.x.subtract(p3.x)).subtract(p1.y).mod(p);
return p3;
}
/**
* 倍乘
* @param p
* @param n
* @return np
*/
ECPoint multiply(ECPoint p, BigInteger n) {
ECPoint q = add(p, new ECPoint());
ECPoint r = new ECPoint();
do {
if (n.and(new BigInteger("1")).intValue() == 1)
r = add(r, q);
q = add(q, q);
n = n.shiftRight(1);
} while (n.intValue() != 0);
return r;
}
/**
* 求階
* @param p 生成元
* @return p對應的階
*/
BigInteger o(ECPoint p) {
BigInteger r = new BigInteger("1");
while (! p.isO()) {
r = r.add(new BigInteger("1"));
p = multiply(p, r);
}
return r;
}
/**
* 求所有解點
* @return
*/
List<ECPoint> solutionPoints() {
List<ECPoint> r = new ArrayList<ECPoint>();
List<BigInteger> l = new ArrayList<BigInteger>();
for (BigInteger y = new BigInteger("1"); y.compareTo(p.divide(new BigInteger("2"))) != 1; y = y.add(new BigInteger("1")))
l.add(y.modPow(new BigInteger("2"), p));
for (BigInteger x = new BigInteger("0"); x.compareTo(p) == -1; x = x.add(new BigInteger("1"))) {
BigInteger t = x.pow(3).add(a.multiply(x)).add(b).mod(p);
if (isExist(t, l) != -1) {
BigInteger y = new BigInteger(isExist(t, l) + "");
r.add(new ECPoint(x, y));
r.add(new ECPoint(x, p.subtract(y)));
}
}
r.add(new ECPoint());
return r;
}
static int isExist(BigInteger b, List<BigInteger> l) {
for (int i = 0; i < l.size(); i++)
if (l.get(i).compareTo(b) == 0) return (i + 1);
return -1;
}
}
TestECC.java
import java.math.BigInteger;
public class TestECC {
public static void main(String[] args) {
BigInteger M = new BigInteger("1234567890abcdef", 16);
System.out.println("明文:M=" + M.toString(16));
ECC_p ecc;
BigInteger k = new BigInteger("abcdef", 16);
BigInteger[] C;
ECPoint X1;
System.out.println("P-192");
ecc = new ECC_p(192);
C = ecc.encrypt(M, k);
X1 = new ECPoint(C[0], C[1]);
System.out.println("密文:(" + X1 + ", " + C[2].toString(16) +")");
M = ecc.decrypt(C);
System.out.println("明文:M=" + M.toString(16));
System.out.println("P-224");
ecc = new ECC_p(224);
C = ecc.encrypt(M, k);
X1 = new ECPoint(C[0], C[1]);
System.out.println("密文:(" + X1 + ", " + C[2].toString(16) +")");
M = ecc.decrypt(C);
System.out.println("明文:M=" + M.toString(16));
System.out.println("P-256");
ecc = new ECC_p(256);
C = ecc.encrypt(M, k);
X1 = new ECPoint(C[0], C[1]);
System.out.println("密文:(" + X1 + ", " + C[2].toString(16) +")");
M = ecc.decrypt(C);
System.out.println("明文:M=" + M.toString(16));
System.out.println("P-384");
ecc = new ECC_p(384);
C = ecc.encrypt(M, k);
X1 = new ECPoint(C[0], C[1]);
System.out.println("密文:(" + X1 + ", " + C[2].toString(16) +")");
M = ecc.decrypt(C);
System.out.println("明文:M=" + M.toString(16));
System.out.println("P-521");
ecc = new ECC_p(521);
C = ecc.encrypt(M, k);
X1 = new ECPoint(C[0], C[1]);
System.out.println("密文:(" + X1 + ", " + C[2].toString(16) +")");
M = ecc.decrypt(C);
System.out.println("明文:M=" + M.toString(16));
}
}