RSA算法是1977年由Ron Rivest、Adi Shamir 和 Leonard Adleman三人組在論文A Method for Obtaining Digital Signatures and Public-Key Cryptosystems提出的公鑰加密算法。因爲加密與解密使用不一樣的祕鑰,從而回避了祕鑰配送問題,還能夠用於數字簽名。該算法的誕生很大程度上有受到了論文New Directions in Cryptography(由Whitfield Diffie和Martin Hellman兩人合做發表)的啓發,關於RSA誕生背後的趣事見RSA 算法是如何誕生的。html
原論文能夠說思路很是清晰、淺顯易懂,是學習該算法很是不錯的英文資料。原文首先梳理了公鑰加密系統和數字簽名的特性和需知足的要求(這部分是其實是借鑑了Whitfield Diffie和Martin Hellman的思想);而後闡述如何利用不一樣的加密祕鑰和解密祕鑰實現加解密流程,這是RSA算法工做的核心部分;接着介紹其背後的數學原理並證實算法的正確性,主要涉及基礎數論知識(例如歐拉函數,費馬定理,歐拉定理等);爲了使算法更具備操做性,還介紹瞭如何利用"反覆平法"算法進行快速的計算冪取模,從而快速的進行加解密運算,以及算法中涉及到其餘參數選取(例如大素數$p,q$選取,$e$和$d$的選取和計算);同時也用一個簡單的小實例模擬算法的流程;最後一個重要的話題是討論該算法的安全性,經過考慮不一樣方法嘗試破解該算法,並從計算量的角度解釋了破解該算法的困難程度,以及其餘的細節bla bla bla。。。。git
因此原論文幾乎是比較全地闡述了RSA算法的方方面面且不失可讀性,很是值得借鑑。一些中文書籍也對該算法作了淺顯易懂的解釋,好比密碼編碼學與網絡安全原理與實踐和圖解密碼技術兩本書的相關章節,都是很好的學習資料。算法
公鑰加密系統主要特色是採用不一樣的加密祕鑰和解密祕鑰。在該系統中每一個用戶均生成本身的加密祕鑰和解密祕鑰,其中加密祕鑰需公開出去,任何人均可以得到,加密祕鑰也稱爲公鑰;解密祕鑰必須保密妥善保管,解密祕鑰也稱爲私鑰。在進行通訊的時候,發送方先用接收方的加密祕鑰(也就是公鑰)加密消息,獲得密文消息,發送給接收方;接收方收到消息用本身的解密祕鑰(也就是私鑰)能夠解密獲得明文。採用這樣的加解密方式從而規避了傳統加密系統中祕鑰配送問題。安全
在密碼學中,加密算法每每都是公開的,嘗試經過保密加密算法來得到加密安全性的作法都是不值得推薦的(此所謂隱蔽式安全)。事實上,發明一個可行的加密算法並不是易事,將其公之於衆經受時間和廣大使用者的檢驗是很好的作法。加密算法在祕鑰的配合使用下進行加密和解密。所以在加密算法是公開的前提下,保證加密的安全依賴於祕鑰的安全性。網絡
將加密過程(encryption)和解密過程(decryption)分別視爲一種處理程序,分別用$E$和$D$表示表示。明文消息和密文消息分別用$M$和$C$表示。則公鑰加密系統有以下四種特性:app
(a)對於加密後的密文$C=E(M)$,對應的解密程序可以處理獲得明文:$D(C)=D(E(M))=M$。函數
(b)加密過程$E$和解密過程$D$是容易計算的。學習
(c)由公開的加密程序$E$不能輕易的獲得解密程序$D$,這樣能夠保證由$E$加密的消息只能由$D$解密。測試
(d)對明文消息先解密處理在作加密處理仍能夠獲得明文,也即保證逆向處理的可行性:$E(D(M))=M$。ui
特性(a)保證了加密的原始目的,若是加密後的密文不能由接受者解密還原回明文,那彼此就不能正常通訊了。特性(a)和(b)在傳統的對稱加密系統中也是成立的。特性(d)用於數字簽名,之因此可以對明文進行解密處理這實際上並不奇怪:拋開加密與機密的概念,$E$和$M$實際上就是一個從輸入到輸出的映射,明文和密文的概念是站在人的立場行劃分的,對於計算機,不管是明文仍是密文,都是比特序列,並無其餘任何差異,所以對明文作加密無非是進行一次函數運算。
一、祕鑰生成
每個用戶都會生成本身的公鑰和私鑰,其流程以下:
1)選取兩個大的素數$p$和$q$。
2)計算$p$和$q$的乘積$n=p \times q$。
3)隨機選取一個與$\phi (n)=(p-1)\times(q-1)$互質的數$e$,也便是$gcd(d,(p-1)\times(1-1))=1$,應用中一般選取$e=65537$。
4)計算$e$模$\phi (n)$的模反元素$d$,也便是計算知足$e\cdot d = 1\;(mod\;\phi (n))=e\cdot d = 1\;(mod\;(p-1)\cdot (q-1))$的$d$。
5)將$(e,n)$公開做爲公鑰,任何人均可以獲取;將$(d,n)$做爲私鑰,本身妥善保存。
可見,在RSA加密算法中,公鑰以一個正整數對的形式出現,同理私鑰也是如此。
在祕鑰生成過程當中,會產生以下的信息:
$p,q,n,\phi (n),d,e$
可是須要公開的信息僅僅是$e,n$兩個整數,其餘信息都應該嚴格的保密。
(以上流程實際上與原論文中有細微差異:原論文中是選取$d$而後來計算$e$,可是在如今的許多公鑰證書中,$e$基本都是相同的,也就是說如今的應用中實際是選取$e$而後計算$d$。)
二、加密和解密
仍是以Alice和Bob這兩個密碼學中的兩個網紅爲角色,述闡RSA算法加密和解密的流程。假設Alice向Bob發起通訊,且已經獲取到Bob公鑰對$(e,n)$。
1)Alice首先將明文分解成若干塊,每一個塊能夠表示爲一個整數(也就是將長比特序列分解成若干短的比特序列,每一個序列天然可表示爲一個整數),以$M$表示,使得$1 \leqslant M \leqslant n-1$。爲方便起見,只考慮對一個塊進行加密的流程。
2)Alice用Bob的公鑰$(e,n)$作以下運算,獲得密文$C$,將密文經過公網通道發送到Bob。
$C = M^{e}\;mod\;n$
3)Bob收到密文,用本身的私鑰$(d,n)$作以下運算,可獲得明文$M$.
$M=C^{d}\;mod\;n$
至此,加解和解密的流程已經結束,流程也很是簡單清晰。
用下圖總結祕鑰生成和加解密的流程:
接下來的問題在於Bob必定可以解密獲得明文$M$嗎?其原理何在呢?答案是確定的,其原理這正是下一節須要解釋的內容。
要證實Bob可以解密獲得明文$M$,須要用到一點簡單的基礎數論知識,例如歐拉函數,費馬定理,歐拉定理,不過這些知識都是比較容理解的,以前已經梳理過這方面的知識,見現代密碼學中的數論基礎知識梳理。
爲了更清晰的認識如今要作的工做,不仿將問題抽象成以下表述:
已知條件:
$p,q$是素數,$n=p \times q$
$d$與$(p-1)\cdot (q-1)$互質,且$e,d$知足$e\cdot d = 1\;(mod\;\phi (n))=e\cdot d = 1\;(mod\;(p-1)\cdot (q-1))$
$C = M^{e}\;mod\;n,1 \leqslant M \leqslant n-1$
求證:
$M=C^{d}\;mod\;n$
證實:
由$e\cdot d = 1\;(mod\;\phi (n))$得$e\cdot d=k\phi (n)+1$($k$爲某正整數)。
將$C \equiv M^{e}\;(mod\;n)$簡單等價變換得:
$C^{d} \equiv M^{e\cdot d} \equiv M^{k\phi (n)+1}\;(mod\;n)$
如今須要證實$M^{k\phi (n)+1} \equiv M\;(mod\;n)$便可。
(1)當$M$與$n$互質時,由歐拉定理得:
$M^{\phi (n)}\equiv M^{(p-1)(q-1)}\equiv 1\;(mod\;n)$
簡單等價變換得:
$(M^{\phi (n)})^{k}\cdot M\equiv M^{k\phi (n)+1}\equiv M\;(mod\;n)$由此得證。
(2)當$M$與$n$不互質時,因爲$n$的素因子分解只能是$n=p \times q$,因此$gcd(M,n)$或者是$p$或者是$q$,也便是$M=hp$或者$M=hq$。
假設$M=hq$,此時$M$必然與$p$互質。
由費馬定理和歐拉函數可知:
$M^{p-1}\equiv1\;(mod\;p)$
通過簡單的等價變換得:
$(M^{p-1})^{k(p-1)}\cdot M\equiv M^{k\phi (n)+1}\equiv M\;(mod\;p)$
也便是:
$(hq)^{k\phi (n)+1}= jp+hq$($h$爲某正整數)
上式的左邊必然是$q$的整數倍,因此右邊也必然是$q$的整數倍,能夠推斷$jp$必然是$q$的整數倍;由於$p,q$互質的關係,能夠推斷$j$必然是$q$的整數倍,也便是$j=t \cdot q$,因此繼續整理上式得:
$(hq)^{k\phi (n)+1}= tqp+hq=tn+hq$($t$爲某正整數)
由此獲得:$M^{k\phi (n)+1}\equiv M\;(mod\;n)$
同理,$M=hp$時能夠獲得相同的結論。
綜合(1)和(2)能夠得出結論:$M^{k\phi (n)+1}\equiv M\;(mod\;n)$老是成立的,因此$M=C^{d}\;mod\;n$也是成立的,證畢。
以上證實過程(2)會比較難理解一點,原論文中對這個證實過程的處理會比較晦澀一點,但基本上也是以上證實思路,因此本文特意針對該證實儘可能細化處理。
如下用一個比較具體的實例來模擬該算法。
1)首先Bob生成公鑰和私鑰
參數選取$p=887,q=911,n=p \times q=808057,\phi (n)=(p-1)(q-1)=806260,e=65537$。
利用擴展歐幾里德算法求$65537d-y\phi (n)=1$,得:$65537\times 158233+(-12862)\times 806260=1$,因此求得$d=158233$。
因而Bob的公鑰爲$(e,n)=(65537,808057)$,私鑰爲$(d,n)=(158233,808057)$。
2)Alice用Bob的公鑰加密明文消息$M=123456$獲得密文,$C=M^{e}\;mod\;n=123456^{65537}\;mod\;808057=147690$,將$C$發送給Bob。
3)Bob收到密文消息,用本身私鑰解密,$M=C^{d}\;mod\;n=815453^{158233}\;mod\;808057=123456$,Bob解密成功。
實際上,真實應用中的$n$很大,每每是1024位的二進制數(大約至關於$1024\times log_{10}2 \approx 309$位十進制數),或者是2048位,甚至是4096位的大數。
以上內容大體解釋了該算法的流程和原理,但實際上還有不少的細節值得思考,好比加解密如何快速計算冪取模,$p,q$兩個大素數如何有效選取,模反元素$d$如何計算等等,這些細節的處理的對該算法的實踐性很重要。
一、快速冪取模算法
加密和解密過程實際上都是冪取模的操做,也便是計算:$a^b\;mod\;n$。實際應用中各參數都是極大的整數,可以找到一種高效的算法呢?
確實有一種算法可以比較高效計算冪取模,稱爲「反覆平方」算法,在算法導論中也有描述該算法。其算法流程以下:
反覆平方法的Demo實現:
public static long doModularExponentiation(long a, long b, long n) { long digit = Long.toBinaryString(b).length();// 獲得b的二進制表示位數 long mask = 1 << (digit - 1); long remainder = 1; for (long i = digit - 1; i >= 0; i--) { remainder = (remainder * remainder) % n; if (mask == (b & mask)) {// 當該位爲1時,須要補充考一次a remainder = (remainder * a) % n; } mask >>= 1; } return remainder; }
mask的做用就是判斷指數$b$的二進制表示中當前輪次所對應的二進制位(從高位算起)是否爲1,每輪事後mask會右移一位。
例如求$147690^{158233}\;mod\;808057$,在doModularExponentiation(147690,158233,808057)計算過程當中,指數$b$和各輪次mask的二進制表示以下:
100110101000011001 100000000000000000 10000000000000000 1000000000000000 100000000000000 10000000000000 1000000000000 100000000000 10000000000 1000000000 100000000 10000000 1000000 100000 10000 1000 100 10 1
二、挑選大素數和素數測試
素數的分佈規律還沒有被徹底研究清楚,可是已經有一些有意思的結論,例如素數定理代表:
$\lim_{n \to \infty }\pi (n)=\frac{n}{ln(n)}$
其中$\pi (n)$爲素數分佈函數,表示小於等於$n$的素數的個數。
當$n$很大時,$\frac{n}{ln(n)}$的結果比較接近真實值。定理代表隨機選取一個整數爲素數的機率爲$\frac{1}{ln(n)}$,從機率分佈的角度,能夠認爲$ln(n)$中就有一個是素數。所以要找一個長度與$n$相同的素數,大約須要檢測$n$附近的$ln(n)$個整數就能找到,好比要找一個2048二進制位的素數,能夠檢測$ln2^{2048}\approx 1420$個隨機選取的2048位整數的素性便可。
測試素性是一個比挑選素數更加複雜的工做,費馬測試是一個基本的思路,但尚有瑕疵,會存在誤判的狀況(例如$7^{560}\equiv 1\;(mod\;561)$,可是561是一個合數,561=3*11*17,還有像341,645,1105等,被這樣誤判的數稱爲Carmichael數);Miller-Rabin測試精確度會更高。事實上,測試大素數是一個機率性的工做。
三、挑選$e$和計算$d$
實際應用中不少公鑰證書中的$e$一般選擇$(010001)_{16}=(65537)_{10}$。
由$e$計算$d$並不是難事,因爲$ed\equiv\;1(mod\;\phi(n))$,也就是解線性方程$ed+(-y)\phi (n)=1=gcd(e,\phi (n))$,利用擴展歐幾里德算法能夠快求解。
擴展歐幾里德算法Demo實現:
public static long[] gcdExt(long a,long b){ long ans; long[] result=new long[3]; if(b==0) { result[0]=a; result[1]=1; result[2]=0; return result; } long [] temp=gcdExt(b,a%b); ans = temp[0]; result[0]=ans; result[1]=temp[2]; result[2]=temp[1]-(a/b)*temp[2]; return result; }
數字簽名是紙質簽字的電子化。
數字簽名涉及兩個問題:首先消息的接受者要可以確認消息來源,也就是確認該消息確實是所指望的人發送的;其次接受者也可以說服第三方來驗證此消息確實是簽名者發送的而並不是是其餘人僞造的簽名,這樣能夠反駁發送者的否定行爲。數字簽名必須同時是消息依賴(可以識別篡改)和簽名人依賴的。數字簽名其重要意義在於特定的簽名者與特定的消息綁定在一塊兒。
數字簽名依賴於特性(d),加密算法會做用在未加密的明文消息上,其實就是加密過程的逆向使用。
因此簽名$S$實際上就是由以下規則計算得出:
$S=D(M)$
這樣Bob將這個數字簽名發送給Alice,Alice能夠根據Bob的公鑰,驗證確實是Bob簽名的,同時,Alice也可讓第三方來驗證這個簽名,由於這個簽名只能由Bob的私鑰計算得出,因此Alice和別人不能僞造簽名,所以Bob也就沒法否定。
因爲祕鑰生成過程當中,只對外公開公鑰對$(e,n)$,因此破解者只能嘗試從這兩個信息試圖去計算得出私鑰$(d,n)$。
破解RSA算法能夠從以下幾個方面作嘗試:
(1)素因子分解$n$
(2)在不分解$n$的前提下計算$\phi (n)$
(3)在不分解$n$和不計算$\phi (n)$的前提下,暴力破解$d$
然而以上問題並不容易,都沒有十分高效的算法求解。
理論上,只要祕鑰空間是有限的,花費大量計算和大量時間都是能夠破解的,然而只要祕鑰長度選取足夠,在現實的時間破解該算法變得不太可能,所以RSA的安全性實際上是在討論一個時效性的問題。隨着硬件計算速度的升級,之前被認爲足夠安全的祕鑰位數會被逐漸攻破,不得不進一步增長祕鑰的長度,所以RSA算法的祕鑰長度廣泛比對稱加密和橢圓曲線加密的祕鑰長度更長。也就是說加密算法的安全等級並不是徹底取決於祕鑰的長度,與加密算法自己的特性和原理有很大的關係。
因爲對公鑰的獲取過程可能會存在中間人攻擊,致使收到的公鑰不是對方的公鑰而是中間人本身的公鑰,對消息的機密性形成威脅。爲避免這樣中間人攻擊,目前廣泛採用公鑰認證的方式,即由權威認證機構頒佈公鑰證書,將公鑰的信任問題交由可信度更高認證機構(CA)去核實,而認證機構自己的可信度由公信力做爲擔保。
RSA公鑰加密算法在運算速度上比對稱加密慢,所以實際運用中並不會用來真正加密消息,而是用做數字簽名和或者在混合密碼系統中與對稱加密配合使用。
在混合密碼系統中組合使用對稱加密和公鑰加密算法:
(1)發送者產生臨時祕鑰,稱爲會話祕鑰,用對稱加密的算法加密消息。
(2)發送者用公鑰加密算法配合接受者的公鑰加密會話祕鑰,而後加密後的消息與加密後的會話祕鑰組合發送給接受者。
(3)接受者分理出加密後的會話祕鑰,並用本身的私鑰解密處會話祕鑰,而後用會話祕鑰在解密加密消息。
一、A Method for Obtaining Digital Signatures and Public-Key Cryptosystems
二、密碼編碼學與網絡安全原理與實踐
三、圖解密碼技術
轉載請註明原文出處:http://www.javashuo.com/article/p-vofdbbui-kd.html