橢圓曲線原理:
橢圓曲線的圖像並不是橢圓形,橢圓曲線源自於求橢圓弧長的橢圓積分的反函數。
定義:
橢圓曲線可用下列方程來表示,其中a,b,c,d為系數。
E: y2 =ax3 + bx2 +cx +d
橢圓曲線運算:(相當於交換群)
A+B:
過曲線上兩點A,B畫一條直線,找到直線與橢圓曲線的的交點,將該交點關於x軸對稱位置的點定義為A+B。
A+A:
畫出曲線在A點的切線,找到該切線與橢圓曲線的交點,將該交點關於x軸對稱位置的點定義為A+A,即2A。
-A:
A點關於X軸對稱位置的點定義為-A。
那A+(-A)怎樣定義?
認為A和-A間的這條直線在無窮遠處相交,這個點圖像上畫不出來,記為O。
基於上邊的運算,如果有橢圓曲線上一個點G,可以求2G,3G,。。。點的坐標。就是說給定G,求xG並不困難。但反過來很難!(橢圓曲線上的離散對數問題)
橢圓曲線上的離散對數問題
本質上就是“已知點xG求數x的問題”。
密碼學中的橢圓曲線
密碼學中的橢圓曲線運算不是在光滑曲線上進行,並不能使用上面的實數域上的橢圓曲線,因為
1. 實數域上的橢圓曲線是連續的,有無限個點,密碼學要求有限點。
2. 實數域上的橢圓曲線的運算有誤差,不精確。密碼學要求精確。
所以我們需要引入有限域上的橢圓曲線。就是說不是在實數域R上,而是在有限域Fp上。
(x,y坐標的取值只能在有限域Fp中取)
Fp中只有p(p為素數)個元素0,1,2 …… p-2,p-1;
Fp 的加法(a+b)法則是 a+b≡c (mod p);即,(a+c)÷p的余數 和c÷p的余數相同。
比特幣中ECDSA使用的橢圓曲線參數:secp256k1
有限域Fp定義:
p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F
= 2256 - 232 - 29 - 28 - 27 - 26 - 24 - 1
Fp上的曲線E:y2 = x3 + ax + b定義如下:
a = 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
b = 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000007
壓縮形式(1字節壓縮標志位+32字節x坐標)的基點G是:
G = 02 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798
未壓縮形式(1字節壓縮標志位+32字節x坐標+32字節y坐標):
G = 04 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798 483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8
參數n,它是使得 nG=0 的最小正整數:
n = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141
參數h,它是橢圓曲線群的階跟由G生成的子群的階的比值。是設計secp256k1時使用的參數,在具體實現中使用這個參數主要是出於安全性考慮,忽略它不影響理解。
h = 01

私鑰公鑰地生成過程:
用戶隨機生成一個小於 n 的大整數 k ,這就是私鑰。
然后計算 Q=kG ,這就是公鑰(注意,公鑰是橢圓曲線上的一個點)。
ECDH過程:
假設公私鑰是用於密鑰交換,那么步驟如下(這里的乘法是指橢圓曲線上點的乘法):
part1:
小紅生成私鑰kA,將它乘以基點G得到公鑰QA,即 kA *G=QA
小明生成私鑰kB,將它乘以基點G得到公鑰QB,即 kB *G=QB
part2:
小紅計算 ( xk, yk ) = kA *QB, xk即為交換得到的密鑰。
小明計算 ( xk, yk ) = kB *QA, xk即為交換得到的密鑰。
最后,小紅跟小明得到的密鑰是相同的。
由以上過程,ECDH的part2部分最終交換得到的密鑰是只用到曲線上的點32字節的x坐標值,是沒用到y坐標的。
而part1部分,這個公鑰QA則必須是全的xy坐標都有。
注:ledger源碼中由32字節ECC私鑰生成65字節(1+32x+32y)ECC公鑰的過程實際只用到了part1,只要得到Q的x和y坐標。
javacard中的ECDH
javacard.security.KeyAgreement
KeyAgreement類是密鑰協商算法的基類,例如Diffie-Hellman和EC Diffie-Hellman [IEEE P1363]。 KeyAgreement算法的實現必須擴展此類並實現所有抽象方法。撕裂或卡重置事件將初始化的KeyAgreement對象重置為先前通過調用init()初始化時所處的狀態。
在用這個類實現ECDH時,
首先,
private KeyAgreement keyAgreement;
keyAgreement=KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN_XY,false);
getInstance
功能是創建所選算法的KeyAgreement對象實例。
public static final KeyAgreement getInstance(byte algorithm,
boolean externalAccess)
throws CryptoException
algorithm - 所需的密鑰協商算法:例如:ALG_EC_SVDP_DH_PLAIN_XY

externalAccess - 如果為true,則表示實例將在多個applet實例之間共享,並且當KeyAgreement實例的所有者不是當前選定的applet時,也將訪問KeyAgreement實例(通過Shareable接口)。如果為true,則實現不得為內部數據分配CLEAR_ON_DESELECT瞬態空間。
然后,
keyAgreement.init(privateKey)
privateKey存了對應於小紅的私鑰kA
注意:
這個privateKey一定要在之前設定好secp256k1對應的參數(ledger源碼中定義了Secp256k1類,只需調用Secp256k1.setCommonCurveParameters(privateKey)設置參數),
並且也放入了私鑰值kA(調用setS()來存)
init()
功能是使用給定的私鑰初始化對象。
檢查密鑰與KeyAgreement算法的一致性。例如,密鑰類型必須匹配。對於橢圓曲線算法,key必須表示曲線域參數上的有效點。其他關鍵組件/域參數強度檢查是特定於實現的。
接着,就是流程中小紅生成QA(QA結果存放在publicPoint中),
keyAgreement.generateSecret(Secp256k1.SECP256K1_G, (short)0,
(short)Secp256k1.SECP256K1_G.length, publicPoint, publicPointOffset);
generateSecret
函數generateSecret就是實現密鑰交換中的這兩部分的曲線上乘法,例如kA*G=QA
輸出的結果根據所選擇的算法不同是不同的,具體的:
帶有_PLAIN:輸出結果就是原始的,與之對應不帶有PLAIN的則是輸出將原始結果通過SHA-1散列后得到的20字節的結果。
帶有_XY:輸出結果是未壓縮完整的結果QA(65字節),與之對應的不帶有_XY的則是壓縮形式的結果(只有QA的x坐標,即32字節)
帶有_DHC:和帶有_DH的輸出是一樣的,只是DHC是 with cofactor multiplication and compatibility mode