橢圓曲線加密算法依賴於橢圓曲線理論,後者理論涵蓋的知識比較深廣,並且涉及數論中比較深奧的問題。通過數學家幾百年的研究積累,已經有不少重要的成果,一些很棘手的數學難題依賴橢圓曲線理論得以解決(好比費馬大定理)。html
本文涉及的橢圓曲線知識只是抽取與密碼學相關的很小的一個角落,涉及到很淺的理論的知識,同時也是一點比較膚淺的總結和認識,重點是利用橢圓曲線結合數學技巧闡述加密算法的過程和原理。java
本文特地構造有比較多的實例方便理解其過程和原理。算法
橢圓曲線方程來源於橢圓積分,後者來最初來源於計算橢圓周長的問題,有一段時間的歷史了,在歐拉時期就開始研究。橢圓周長沒有精確的初等函數的公式表示,只有近似的公式表示,精確的橢圓周長能夠用不定積分表示。安全
如今通常將形如以下形式的積分定義爲橢圓積分:網絡
$f(x)=\int_{c}^{x}R\left [ t,\sqrt{P(t)} \right ]d_{t}$函數
其中$R$是其兩個參數的有理函數,$P$是一個無重根的3或4階多項式,而$c$是一個常數。橢圓曲線方程與$P(t)$表現形式比較相像。學習
數學上的橢圓曲線通常由以下形式給出:ui
$E:y^{2}=x^{3}+ax^{2}+bx+c$,其中判別式$\Delta (E)=-4a^{3}c+a^{2}b^{2}-4b^{3}-27c^{2}+18abc\neq 0$this
橢圓曲線都是關於X軸對稱的曲線。編碼
典型的橢圓曲線如:$y^{2}=x^{3}-4x^{2}+16$,其圖像爲:
更多的橢圓曲線圖像:
限定$\Delta$不爲零有特殊的意義。若是判別式$\Delta (E)$等於零,由三次方程判別式斷定理可知,方程$x^{3}+ax^{2}+bx+c=0$存在二重根或者三重根,曲線表現爲"自相交"或者有「尖點」。
兩個典型的例子是:
$C1:y^{2}=x^{3}$
$C2:y^{2}=x^{3}+x^{2}$
$C1$有三重根,表現爲有"尖點";$C2$有二重根,表現爲「自相交」,它們都不是橢圓曲線,其圖像分別以下:
在密碼學中用到的橢圓曲線方程通常限定爲:
$E:y^{2}=x^{3}+ax+b$,其中$4a^{3}+27b^{2}\neq 0$。
也便是這裏的二次項係數爲0。
橢圓曲線上能夠定義一些頗有意思的特殊運算規則。通常來講會定義兩種運算:加法和數乘運算。加法運算是點與點之間的運算;數乘運算基於加法運算,重複的加法運算就是數乘。
一、實數域的加法運算
1.一、加法運算的幾何解釋
已知橢圓曲線上兩個不一樣的點$P$和$Q$,則這兩個點之和$R=P+Q$能夠經過以下操做獲得:過$P$、$Q$兩點作直線$L$,與橢圓曲線相交於第三點,該點關於X軸的對稱點便是所求的$R$點。橢圓曲線的這種加法運算有比較明確的幾何含義。以下所示:
以這種比較奇特的規則來定義加法運算會讓人以爲比較怪異,其思想極可能是借鑑於求橢圓曲線有理解的方法(沒有去嚴格考據)。
求橢圓曲線有理解考慮的問題是尋找有理點$(x,y)$使其知足橢圓曲線方程。其求解過程是在有限的已知有理點的集合中,選兩個點$P1,P2$,做直線與橢圓曲線相交與第三點個有理點$Q3$。此時若是再利用$P1,P2,Q3$三個點中的任意兩點做直線不能在產生新的有理解(由於他們自己是已經在一條直線上,不會產生新的交點),可是考慮$Q3$關於X軸對稱的點$Q'3$一定也是有理點,因而能夠利用$Q3'$與$P1$或者$Q3'$與$P2$繼續作直線與橢圓曲線相交獲得新的有理解,對新的交點再取對稱點,以此迭代下去。由此利用交點的對稱點做直線來生成新的交點,進而可逐步求解知足橢圓曲線的有理解。
橢圓曲線加法運算的規則中「取交點的對稱點」正是與上述求解過程及其類似。
對於加法運算也有另一種描述:若橢圓曲線上三個點在同一直線上,則他們的和爲$O$,也便是$P+Q+R'=O$,其中的$O$是無窮遠點或者零點。
更完整的橢圓曲線加法運算規則以下:
一、$O+O=O$,對任意的$P$,有$P+O=P$;$O$看作零點,對加法運算沒有實際貢獻(相似於四則運算加法運算中的0)。
二、$P=(x,y)$的負元是關於X中對稱的點$-P=(x,-y)$(而不是關於原點對稱),$P+(-P)=O$。過$P$和$-P$的直線與X軸垂直,實際上能夠看作它與橢圓曲線相交於無窮遠點(射影平面,也便是在歐式平面上添加了無窮遠點和無窮遠直線的平面),所以將也將$O$視做無窮遠點。
三、計算$P$和$Q$的和是經過作過$P$和$Q$兩點的直線,與橢圓曲線相交於第三點,再取該點關於X軸的對稱點以此做爲$P,Q$之和,正如上面的幾何圖形展現的那樣。
四、計算$P$點($P \neq O$)的兩倍時,是作該點的切線,再取交點$S$關於X軸的對稱點$-S$,也便是$2P=P+P=-S$
容易驗證,對於橢圓曲線上的點和$O$點組成的集合,以及集合上定義的二元加法運算,構成一個Abel羣。單位元是$O$點,$P(x,y)$的逆元是$P(x,-y)$,封閉性,結合性以及交換性也是顯然知足的。
1.二、加法運算的代數解釋
幾何解釋更直觀,代數解釋更有利於數值計算。
過曲線上$P(x_{p},y_{p})$和$Q(x_{Q},y_{Q})$兩點($P$和$Q$不互爲負元)作直線,求與曲線的第三個交點的問題是很容易用代數的方法來描述的。
也便是求:
$\left\{\begin{matrix}y^{2}=x^{3}+ax+b & (1)\\ y-y_{p}=k(x-x_{p})& (2) \end{matrix}\right.$
其中斜率$k=\frac{y_{Q}-y_{P}}{x_{Q}-x_{P}}$。
將(2)代入(1)再利用次數對齊的方式容易求得第三個交點的對稱點也即$P,Q$之和$R(x_{R},y_{R})$爲:
$x_{R}=k^{2}-x_{P}-x_{Q}$
$y_{R}=-y_{P}+k(x_{P}-x_{R})$
若是須要計算倍乘,可讓多個點自身重複相加獲得。例如$P+P=2P=R$,當$y_{P}\neq 0$時,代數描述爲:
$x_{R}=\left ( \frac{3x^{2}_{P}+a}{2y_{P}} \right )^{2}-2x_{P}$
$y_{R}=\left ( \frac{3x^{2}_{P}+a}{2y_{P}} \right )(x_{P}-x_{R})-y_{P}$
二、模素數P的加法運算
密碼學中廣泛採用的是有限域上的橢圓曲線,也便是變元和係數均在有限域中取值的橢圓曲線。使用模素數$p$的有限域$Z_{p}$,將模運算引入到橢圓曲線算術中,變量和係數從集合${0,1,2,...,p-1}$中取值而非是在實數上取值。
此時討論橢圓曲線形式以下:
$y^{2} \mod p=(x^{3}+ax+b)\mod p$
其中$(4a^{3}+27b^{2})\mod p\neq 0\mod p$,變量和係數均在$Z_{p}$中取值。
將知足上式的全部非負整數對和$O$點記爲集合$E_{p}(a,b)$,這是一個有限的離散點集。由此可知集合中的點分佈在$(0,0)$到$(p-1,p-1)$的象限中,集合中的點有可能恰好也在橢圓曲線上,更多的多是在橢圓曲線外。例如點$(13,7)$是知足$y^{2} \mod 23=(x^{3}+x+1)\mod 23$的點,可是$(13,7)$並不在橢圓曲線上。
實際上,集合$E_{p}(a,b)$與模$p$的加法運算構成循環阿貝爾羣,其生成元,階和生成子羣問題在本節後面會討論。
對於較小的素數$p$,徹底能夠暴力窮舉找出集合$E_{p}(a,b)$中的點。好比參數$a=1,b=3,p=23$,$E_{23}(1,3)$有27個點(包含O點),暴力窮舉這些點分別爲(第八節給出了一些分析橢圓曲線問題的demo實現):
(0,7) (6,15) (15,9) (0,16) (7,10) (15,14) (2,6) (7,13) (19,2) (2,17) (10,1) (19,21) (4,5) (10,22) (21,4) (4,18) (12,8) (21,19) (5,8) (12,15) (22,1) (5,15) (14,1) (22,22) (6,8) (14,22) O
$E_{p}(a,b)$上的加法規則和實數域上的加法基本一致,只是多加了模運算。可是模$p$的加法沒有顯而易見的幾何解釋,只有代數描述。
求解$(x_{R},y_{R})$的代數表達式爲:
$x_{R}=(\lambda ^{2}-x_{P}-x_{Q})\mod p$
$y_{R}=(\lambda(x_{P}-x_{R})-y_{P})\mod p$
其中
$\lambda=\left\{\begin{matrix}(\frac{y_{Q}-y_{P}}{x_{Q}-x_{P}})\mod p & (P\neq Q) \\ (\frac{3x^{2}_{P}+a}{2y_{P}})\mod p & (P= Q)\end{matrix}\right.\\$
例如$a=1,b=1,p=23,P(3,10),Q(13,16)$,求$R=P+Q$.
此時$P\neq Q$,計算$\lambda=(\frac{y_{Q}-y_{P}}{x_{Q}-x_{P}})\mod p=(\frac{16-10}{13-3})\mod 23=6\times 10^{-1}\mod 23$.
要計算上式首先要計算$10^{-1}\mod 23$.
令$x\equiv 10^{-1}(\mod 23)$,因爲$10 \equiv 10(\mod 23)$,因此$10x\equiv 1(\mod 23)$,利用擴展歐幾里德算法求得$x=7$.
$\lambda=6\times 7 \mod 23 = 19$
因此
$x_{R}=(\lambda ^{2}-x_{P}-x_{Q})\mod p=(19^{2}-3-13)\mod 23=345\mod 23=0$
$y_{R}=(\lambda(x_{P}-x_{R})-y_{P})\mod p=(19\times (3-0)-10)\mod 23=47\mod 23=1$
因此$R=(0,1)$.
還能夠按照以上規則計算$2P,3P$等等倍乘點。
實際上$E_{23}(1,1)$中共有28個點(包含無窮遠點$O$),以$P(3,10)$開始的全部倍乘點:$P,2P,3P...27P,28P$能夠暴力計算得出:
P=(3,10) 2P=(7,12) 3P=(19,5) 4P=(17,3) 5P=(9,16) 6P=(12,4) 7P=(11,3) 8P=(13,16) 9P=(0,1) 10P=(6,4) 11P=(18,20) 12P=(5,4) 13P=(1,7) 14P=(4,0) 15P=(1,16) 16P=(5,19) 17P=(18,3) 18P=(6,19) 19P=(0,22) 20P=(13,7) 21P=(11,20) 22P=(12,19) 23P=(9,7) 24P=(17,20) 25P=(19,18) 26P=(7,11) 27P=(3,13)
28P=O
容易驗證,上述計算過程當中$Q(13,16)$點就是$8P$,$P+Q=P+8P=9P=(0,1)$,與上述計算結果是吻合的,讀者也能夠驗證更多的結果。
如今提出一個問題:$E_{p}(a,b)$中有多少個點呢?這個問題的準確答案並很差回答,可是有一些粗略的規律。
設$E_{p}(a,b)$中點的個數爲$N_{p}$,而且$a_{p}=p-N_{p}$,實際上$N_{p}$與$p$比較接近。
Hasse定理代表:
$\left | a_{p} \right |< 2\sqrt{p}$
這實際上解釋了$a_{p}$的偏差,從而有$-2\sqrt{p}< a_{p}=p-N_{p}<2\sqrt{p}$
也就是$p-2\sqrt{p}<N_{p}<p+2\sqrt{p}$,等價於$p+1-2\sqrt{p} \leqslant N_{p}\leqslant p+1+2\sqrt{p}$
接下來的一個問題是子羣、生成元和階:
不可貴知,有限域上的橢圓曲線的點和加法運算構成一個有限交換羣$S$。
以一個點$G$做爲生成元,進行重複的加法運算,可以生成一個子羣$S'$。羣$S'$有可能與$S$相同,更有多是$S$的一個真子羣。
好比橢圓曲線爲$y^{2}=x^{3}+x+1$,$p=23$
以$G=(3,10)$生成的子羣恰好等於$S$,由於$28G=O$,說明此時$S$仍是一個有限循環羣,羣中每一個元素都能由$(3,10)$經過重複的加法運算獲得,羣中元素的個數爲28,其階爲28(最小的使得$nG=O$成立的$n$),生成元爲$(3,10)$。
可是以$G=(6,19)$生成的子羣是$S$的真子羣,由於$14G=O$,它也是一個有限循環羣,由於羣中每一個元素都能由$(6,19)$經過重複的加法運算獲得,該子羣的元素個數爲14,階爲14(最小的使得$nG=O$的成立$n$),生成元爲$(6,19)$。
拉格朗日定理還告訴咱們,生成的子羣$S'$的階與原來的羣$S$的階存在約數關係,也就是說子羣$S'$的階只能是1,2,4,7,14,28。羣$S$的階與子羣$S'$的階的比值通常稱爲協因子(cofactor):$h=\frac{\left | S \right |}{\left | S' \right |}$。通常會取$h=1$,以保證生成子羣的階比較大。
以$G=(6,19)$生成子羣過程(爲方便計算,取$O=(-1,-1)$):
1P=(6,19) 15P=(6,19) 2P=(13,16) 16P=(13,16) 3P=(7,11) 17P=(7,11) 4P=(5,19) 18P=(5,19) 5P=(12,4) 19P=(12,4) 6P=(17,20) 20P=(17,20) 7P=(4,0) 21P=(4,0) 8P=(17,3) 22P=(17,3) 9P=(12,19) 23P=(12,19) 10P=(5,4) 24P=(5,4) 11P=(7,12) 25P=(7,12) 12P=(13,7) 26P=(13,7) 13P=(6,4) 27P=(6,4) 14P=(-1,-1) 28P=(-1,-1)
在後面的ECC加密算法過程當中會有一個給定的基點$G$(也就是生成元)生成一個子羣,而後祕鑰空間在此子羣取得,通常會要求保證子羣的階會盡可能大,基點及其子羣的階$n$都是公開的信息。
構造一個數學難題來保證加密的安全性是現代密碼學中加密算法的主要思想。相似RSA算法中大數的質因子分解難題同樣,橢圓曲線也有相似的數學難題。
考慮$Q=kP$,其中$Q,P\in E_{p}(a,b),k<p$。
對於給定的$k,p$計算$Q$是很容易的;反過來給定$Q,P$,計算$k$是至關困難的,這就是橢圓曲線的離散對數問題(這裏之因此稱之爲離散對數問題大概是爲了與其餘加密算法的說法保持一致,便於理解)。
正由於如此,能夠將$Q$做爲公鑰,公開出去;$k$做爲私鑰,祕密保管,經過公鑰來破解私鑰十分困難。
目前由橢圓曲線公鑰求解私鑰的最有效算法複雜度爲$O(\sqrt{p})$,其中$p$是階數$n$的最大素因子。
一、原理和流程
利用模$p$有限域上橢圓曲線算術規則能夠用來實現祕鑰協商,其流程以下:
(1)Alice和Bob會共享一些橢圓曲線的參數信息:$(p, a, b, G, n, h)$,其中$a,b$肯定了橢圓曲線方程,$p$肯定了模$p$的有限域,G是中的生成元,用於生成子羣,要求$G$的階$n$應該儘可能大,$G$的階是使得$nG=O$成立的最小的整數,也便是$(n-1)G$所表明的點與$G$點的橫座標恰好相同,協因子$h$通常等於1。
(2)Alice選擇小於$n$整數$n_{A}$,而後計算$P_{A}=n_{A}\times G$,將$P_{A}$發送給Bob。
(3)同理,Bob也選擇小於$n$整數$n_{B}$,而後計算$P_{B}=n_{B}\times G$,將$P_{B}$發送給Alice。
(4)Alice經過以下計算得出協商的祕鑰:$K_{A}=n_{A}\times P_{B}$;Bob經過以下計算得出協商的祕鑰:$K_{B}=n_{B}\times P_{B}$,容易證實,$K_{A}=K_{B}$。
由於$K_{A}=n_{A}\times P_{B}=n_{A}\times (n_{B}\times G)=n_{B}\times (n_{A}\times G)= K_{B}$。
由此Alice和Bob計算獲得相同的祕鑰,達到祕鑰協商的目的。
二、實例
例如,參數$a=0,b=-4$,橢圓曲線方程爲$y^{2}=x^{3}-4$,$p=211,G=(2,2)$,由於$240G=(2,209)$因此$n=241$。
Alice選擇$n_{A}=151$,$P_{A}=151G=(62,59)$;
Bob選擇$n_{B}=171$,$P_{B}=171G=(209,153)$;
他們協商的祕鑰$K=151P_{B}=171P_{A}=151(209,153)=171(62,59)=(95,194)=(151*171\%n)G=34G$。
一、原理和流程
利用橢圓曲線來進行加密和解密也是與RSA必定程度的相似,每個用戶都有屬於本身的公鑰和私鑰。私鑰就是用戶選定的數字$n$,私鑰本身保存;公鑰就是由$P=nG$,計算出來的點,公鑰公開。
假設Alice與Bob進行加密通訊,其加密的流程以下:
(1)Alice首先將明文消息轉換(編碼)爲$E_{p}(a,b)$中的$P_{m}(x,y)$,而後隨機選定一個正整數$k$,而且利用Bob的公鑰$P_{B}$經過以下計算出密文:
$C_{m}=\left \{ kG,P_{m}+kP_{B} \right \}$
所以,密文其實是有兩個點組成。
(2)Bob收到密文$C_{m}$,利用本身的私鑰$n_{B}$進行以下計算,能夠解密獲得明文:
$P_{m}+kP_{B}-n_{B}(kG)=P_{m}+k(n_{B}G)-n_{B}(kG)=P_{m}$
也就是用第二個點$P_{m}+kP_{B}$減去第一個點$kG$與本身的私鑰$n$之積。
二、實例
考慮參數$a=0,b=-4,p=199,G=(2,2)$,橢圓曲線方程爲$y^{2}=x^{3}-4$,
Bob選定的私鑰爲$n_{B}=119$,其公鑰$P_{B}=119G=(183,173)$。
Alice但願將消息$P_{m}=(76,66)$加密後發送給Bob,因而Alice隨機選定正整數$k=133$,並經過Bob的公鑰加密獲得密文:
$C_{m}=\left \{ 133(2,2),(76,66)+133(183,173) \right \}=\left \{ (40,147), (180,163)\right \}$
Bob收到密文消息利用本身的私鑰$n_{B}=119$進行解密:
$P_{m}+kP_{B}-n_{B}(kG)=(180,163)-119(40,147)=(180,163)-(98,52)=(76,66)=P_{m}$.
由此,Bob順利解密獲得明文消息,Alice與Bob之間完成加密通訊。
公鑰加密算法中除了ECC,還有另一個普遍使用的加密算法--RSA公鑰加密算法。
ECC與RSA相比,主要的優勢是在相同的安全級別下ECC使用的祕鑰長度要短不少,由此帶來處理速度、帶寬和存儲空間上的額外優點。下表展現了不一樣加密算法祕鑰的位數對比狀況:
比特幣中使用橢圓曲線密碼算法,以保證比特幣網絡中信息安全性和簽名認證問題。每一個用戶都有屬於本身的公鑰和私鑰。利用私鑰能夠對交易信息進行簽名(ECDSA),其餘人能夠利用其公鑰進行認證,公鑰也用來構造錢包地址。所使用的橢圓曲線是採用了Certicom推薦的橢圓曲線secp256k1 ,其參數由六元組構成:$D=(p,a,b,G,n,h)$,其中$p$是很大的質數,$h$就是協因子,表徵由生成元$G$生成子羣的階與母羣的階的關係,前文有解釋;參數信息都是公開的,有興趣能夠到網上查到。
如下實現一些比較經常使用的方法,主要涉及暴力窮舉$E_{p}(a,b)$中的點(無窮遠點O=(-1,-1)),求有限交換羣的階,橢圓曲線加法運算和乘法運算,以及相關的求模反元素和擴展歐幾里德算法等。主要在學習橢圓曲線加密過程當中,方便快速的構造用例和實驗、驗證。主要實現核心的計算原理,細節部分的處理不免有疏漏。
/** * Date:2018.05.16 * Author:Qcer * Function:ECC * */ package com.demo; import java.util.ArrayList; public class ECCImpl { private long a = 0; private long b = 0; private long p = 0; private ECCImpl() {} public ECCImpl(long a, long b, long p) {// 參數初始化,橢圓曲線方程:y^2=x^3+ax+b的形式, this.a = a; this.b = b; this.p = p; } // 暴力搜索(0,0)到(p-1,p-1)象限且知足模p方程的全部點 public ArrayList search() { ArrayList<Long[]> list =new ArrayList<>(); long left,right; for (long x = 0; x < p; x++) {//i=x from 0 to p-1 for (long y = 0; y < p; y++) {//j=y form 0 to p-1 left = y * y % p; right = (x * x * x + a * x + b) % p; if (left == right) { list.add(new Long[] {x,y}); System.out.println("("+x+","+y+")"); } } } return list; } //獲取橢圓曲線上有限交換羣的階,包含無窮雲巔O public long getOrder() { return search().size()+1; } //自定義求模運算,主要考慮a小於0的狀況 public long mod(long a, long p) { long r = Math.abs(a) % p; return a >= 0 ? r : p - r; } //橢圓曲線上的加法運算,R=P+Q public long[] add(long x1, long y1, long x2, long y2) { if (x1 == -1) {// O+P=P return new long[]{x2,y2}; } if (x2 == -1) {// P+O=P return new long[]{x1,y1}; } long[] point = new long[2]; long ratio = 0;//係數 0<=ratio<=p-1 if (x1 == x2) { if (y1 == y2) {// P==Q ratio = mod((3 *x1 * x1 + a) * (getMMI(2 * y1,p)),p); }else { // P+P=O return new long[] {-1,-1}; } }else {// P!=Q // long flag = (y2-y1)*(x2-x1)>0?1:-1;//錨定正負號 long mask = 1<<63; long flag = (((y2 - y1) & mask) ^ ((x2 - x1) & mask)) == 0 ? 1 : -1; ratio = mod(flag * Math.abs(y2 - y1) * (getMMI(Math.abs(x2 - x1),p)),p); } point[0] = mod(ratio * ratio - x1 - x2, p); point[1] = mod(ratio * (x1 - point[0]) - y1,p); // System.out.println("("+point[0]+","+point[1]+")"); return point; } //橢圓曲線上的乘法運算,乘法運算定義爲重複的加法運算:kP=P+P+P+...+P public long[] multiply(int x, int y, int times) { long[] point = {x,y}; for (long i = 2; i <= times; i++) { point = add(point[0],point[1],x,y); System.out.println(i+"P="+"("+point[0]+","+point[1]+")"); if (point[0] == -1) { return point; } } System.out.println(times+"P="+"("+point[0]+","+point[1]+")"); return point; } //利用擴展歐幾裏算法得求模範元素,ax=1(mod b) => ax+b(-y)=1=gdc(a,b) public long getMMI(long a, long b) {//modular multiplicative inverse long mmi = gcdExt(a,b)[1]; while (mmi < 0) { mmi += b; } return mmi; } //擴展歐幾里得算法:ax+by=g=gcd(a,b) => tuple[1]x+tuple[2]y=gcd(a,b) public long[] gcdExt(long a, long b) { long ans; long[] tuple = new long[3]; if (b == 0) { tuple[0] = a; tuple[1] = 1; tuple[2] = 0; return tuple; } long[] temp = gcdExt(b,a%b); ans = temp[0]; tuple[0] = ans; tuple[1] = temp[2]; tuple[2] = temp[1] - (a / b) * temp[2]; return tuple; } public static void main(String[] args) { // TODO Auto-generated method stub // ECCImpl ecc = new ECCImpl(0, -4, 199);//0, -4, 211 // ecc.search(); // ecc.add(3,10,13,16); ECCImpl ecc = new ECCImpl(1, 1, 23); System.out.println(ecc.getOrder()); // ecc.search(); // ECCImpl ecc = new ECCImpl(4, 20, 29); // ecc.search(); // ecc.multiply(12,19, 28); } }
一、密碼編碼學與網絡安全原理與實踐
二、http://andrea.corbellini.name/
三、數論概述.第四十三章-第四十八章
四、https://mp.weixin.qq.com/s/jOcVk7olBDgBgoy56m5cxQ
五、https://blog.csdn.net/mrpre/article/details/72850598
轉載請註明原文出處:http://www.javashuo.com/article/p-fphtccpf-b.html