2018-06-05

?關于使用python實現RSA加密解密



一、非對稱加密算法

1、乙方生成兩把密鑰(公鑰和私鑰)。公鑰是公開的,任何人都可以獲得,私鑰則是保密的。

2、甲方獲取乙方的公鑰,然后用它對信息加密。

3、乙方得到加密后的信息,用私鑰解密。

二、RSA算法

1977年,三位數學家Rivest、Shamir 和 Adleman 設計了一種算法,可以實現非對稱加密。這種算法用他們三個人的名字命名,叫做RSA算法。從那時直到現在,RSA算法一直是最廣為使用的"非對稱加密算法"。毫不夸張地說,只要有計算機網絡的地方,就有RSA算法。

這種算法非常可靠,密鑰越長,它就越難破解。根據已經披露的文獻,目前被破解的最長RSA密鑰是768個二進制位。也就是說,長度超過768位的密鑰,還無法破解(至少沒人公開宣布)。因此可以認為,1024位的RSA密鑰基本安全,2048位的密鑰極其安全。


三、數學基礎

1、互質關系

如果兩個正整數,除了1以外,沒有其他公因子,我們就稱這兩個數是互質關系(coprime)。比如,15和32沒有公因子,所以它們是互質關系。這說明,不是質數也可以構成互質關系。

關于互質關系,不難得到以下結論:

1.任意兩個質數構成互質關系,比如13和61。

2.一個數是質數,另一個數只要不是前者的倍數,兩者就構成互質關系,比如3和10。

3.如果兩個數之中,較大的那個數是質數,則兩者構成互質關系,比如97和57。

4. 1和任意一個自然數是都是互質關系,比如1和99。

5. p是大于1的整數,則p和p-1構成互質關系,比如57和56。

6. p是大于1的奇數,則p和p-2構成互質關系,比如17和15。

2、歐拉函數

請思考以下問題:

任意給定正整數n,請問在小于等于n的正整數之中,有多少個與n構成互質關系?(比如,在1到8之中,有多少個數與8構成互質關系?)

計算這個值的方法就叫做歐拉函數,以φ(n)表示。在1到8之中,與8形成互質關系的是1、3、5、7,所以 φ(n) = 4。

φ(n) 的計算方法并不復雜,但是為了得到最后那個公式,需要一步步討論。

[if !supportLists]四、[endif]密鑰生成

我們通過一個例子,來理解RSA算法。假設愛麗絲要與鮑勃進行加密通信,她該怎么生成公鑰和私鑰呢?


第一步,隨機選擇兩個不相等的質數p和q。

愛麗絲選擇了61和53。(實際應用中,這兩個質數越大,就越難破解。)

第二步,計算p和q的乘積n。

愛麗絲就把61和53相乘。

n = 61×53 = 3233

n的長度就是密鑰長度。3233寫成二進制是110010100001,一共有12位,所以這個密鑰就是12位。實際應用中,RSA密鑰一般是1024位,重要場合則為2048位。

[if !supportLists]l?[endif]第三步,計算n的歐拉函數φ(n)。

根據公式:

φ(n) = (p-1)(q-1)

愛麗絲算出φ(3233)等于60×52,即3120。

第四步,隨機選擇一個整數e,條件是1< e < φ(n),且e與φ(n) 互質。

愛麗絲就在1到3120之間,隨機選擇了17。(實際應用中,常常選擇65537。)

第五步,計算e對于φ(n)的模反元素d。

所謂"模反元素"就是指有一個整數d,可以使得ed被φ(n)除的余數為1。

ed≡ 1 (mod φ(n))

這個式子等價于

ed - 1 = kφ(n) ?(k∈Z)

于是,找到模反元素d,實質上就是對下面這個二元一次方程求解。

ex +φ(n)y = 1

已知e=17,φ(n)=3120,

17x + 3120y = 1

這個方程可以用"擴展歐幾里得算法"求解,此處省略具體過程。總之,愛麗絲算出一組整數解為 (x,y)=(2753,-15),即 d=2753。

至此所有計算完成。

第六步,將n和e封裝成公鑰,n和d封裝成私鑰。

在愛麗絲的例子中,n=3233,e=17,d=2753,所以公鑰就是 (3233,17),私鑰就是(3233, 2753)。

總結,實際上就是計算n,e,d的過程

pq的作用用于求n==pq,再用 (p-1)(q-1)求φ(n),在φ(n)范圍內隨機選擇即為e,d==e對于φ(n)的模反元素

五、驗證RSA算法的可靠性

公鑰公開,私鑰不公開,故d被破解即RSA算法被破解。

回顧上面的密鑰生成步驟,一共出現六個數字:

p,q,n,φ(n),e,d

這六個數字之中,公鑰用到了兩個(n和e),其余四個數字都是不公開的。其中最關鍵的是d,因為n和d組成了私鑰,一旦d泄漏,就等于私鑰泄漏。

那么,有無可能在已知n和e的情況下,推導出d?

ed=1 (modφ(n))。只有知道e和φ(n),才能算出d。

φ(n)=(p-1)(q-1)。只有知道p和q,才能算出φ(n)。

n=pq。只有將n因數分解,才能算出p和q。

結論:如果n可以被因數分解,d就可以算出,也就意味著私鑰被破解。

可是,大整數的因數分解,是一件非常困難的事情。目前,除了暴力破解,還沒有發現別的有效方法。維基百科這樣寫道:"對極大整數做因數分解的難度決定了RSA算法的可靠性。換言之,對一極大整數做因數分解愈困難,RSA算法愈可靠。

假如有人找到一種快速因數分解的算法,那么RSA的可靠性就會極度下降。但找到這樣的算法的可能性是非常小的。今天只有短的RSA密鑰才可能被暴力破解。到2008年為止,世界上還沒有任何可靠的攻擊RSA算法的方式。

只要密鑰長度足夠長,用RSA加密的信息實際上是不能被解破的。"

舉例來說,你可以對3233進行因數分解(61×53),但是你沒法對下面這個整數進行因數分解。

1230186684530117755130494958384962720772853569595334792197322452151726400507263657518745202199786469389956474942774063845925192557326303453731548268507917026122142913461670429214311602221240479274737794080665351419597459856902143413

它等于這樣兩個質數的乘積:

3347807169895689878604416984821269081770479498371376856892431388982883793878002287614711652531743087737814467999489

    ×

36746043666799590428244633799627952632279158164343087642676032283815739666511279233373417143396810270092798736308917

事實上,這大概是人類已經分解的最大整數(232個十進制位,768個二進制位)。比它更大的因數分解,還沒有被報道過,因此目前被破解的最長RSA密鑰就是768位。

[if !supportLists]六、[endif]加密與解密

有了公鑰和密鑰,就能進行加密和解密了。

1、加密要用公鑰 (n,e)

假設鮑勃要向愛麗絲發送加密信息m,他就要用愛麗絲的公鑰 (n,e) 對m進行加密。這里需要注意,m必須是整數(字符串可以取ascii值或unicode值),且m必須小于n。

所謂"加密",就是算出下式的c:

m^e≡ c (mod n)

愛麗絲的公鑰是(3233, 17),鮑勃的m假設是65,那么可以算出下面的等式:

65^17≡ 2790 (mod 3233)

于是,c等于2790,鮑勃就把2790發給了愛麗絲。

2、解密要用私鑰(n,d)

愛麗絲拿到鮑勃發來的2790以后,就用自己的私鑰(3233, 2753) 進行解密。可以證明,下面的等式一定成立:

c^d≡ m (mod n)

也就是說,c的d次方除以n的余數為m。現在,c等于2790,私鑰是(3233, 2753),那么,愛麗絲算出

2790^2753≡ 65 (mod 3233)

因此,愛麗絲知道了鮑勃加密前的原文就是65。

至此,"加密--解密"的整個過程全部完成。

我們可以看到,如果不知道d,就沒有辦法從c求出m。而前面已經說過,要知道d就必須分解n,這是極難做到的,所以RSA算法保證了通信安全。

你可能會問,公鑰(n,e)只能加密小于n的整數m,那么如果要加密大于n的整數,該怎么辦?有兩種解決方法:一種是把長信息分割成若干段短消息,每段分別加密;另一種是先選擇一種"對稱性加密算法"(比如DES),用這種算法的密鑰加密信息,再用RSA公鑰加密DES密鑰。

七、私鑰解密的證明

a=b(mod c)等價于a/c的余數是b,a mod c ==b

最后,我們來證明,為什么用私鑰解密,一定可以正確地得到m。也就是證明下面這個式子:

c^d≡ m (mod n)

因為,根據加密規則

m^e ≡ c (mod n)

于是,c可以寫成下面的形式:

c = m^e - kn(h∈Z)

將c代入要我們要證明的那個解密規則:


由于(a-b)^n=a^n-C1n a^(n-1)b+C2n a^(n-2)b^2+...+(-b)^n

它等同于求證

m^(ed) ≡ m (mod n)

由于

ed ≡ 1 (mod φ(n))

所以

ed = hφ(n)+1

將ed代入:

m^(hφ(n)+1) ≡ m (mod n)

接下來,分成兩種情況證明上面這個式子。

當m與n互質。

根據歐拉定理,此時

  =====> m^φ(n)=kn+1 (k∈Z)

得到


證明(kn+1)^h*m=m(mod n)展開即可

原式得到證明。

當m與n不是互質關系。

此時,由于n等于質數p和q的乘積,所以m必然等于kp或kq。

以m = kp為例,考慮到這時k與q必然互質,則根據歐拉定理,下面的式子成立:

(kp)^q-1 ≡ 1 (mod q)

進一步得到



將它改寫成下面的等式


這時t必然能被p整除,即 t=t'p


因為m=kp,n=pq,所以


原式得到證明。

[if !supportLists]八、[endif]快速冪模算法

在講解快速冪取模算法之前,我們先將幾個必備的知識

1.對于取模運算:

(a*b)%c=(a%c)*(b%c)%c ?

這個是成立的:也是我們實現快速冪的基礎

核心思想在于:

將大數的冪運算拆解成了相對應的乘法運算,利用上面的式子,始終將我們的運算的數據量控制在c的范圍以下,這樣我們可以客服樸素的算法的缺點二,我們將計算的數據量壓縮了很大一部分,當指數非常大的時候這個優化是更加顯著的,我們用Python來做一個實驗來看看就知道我們優化的效率有多高了

算法實現:

#快速冪模運算,把b拆分為二進制,遍歷b的二進制,當二進制位為0時不計入計算

def quick_pow_mod(a, b, c):

????a = a % c

????ans = 1

#這里我們不需要考慮b<0,因為分數沒有取模運算

????while b != 0:

#判斷b的二進制最后一位數是不是1,是則參與計算

????????if b & 1:

????????????ans = (ans * a) % c

# ans = (ans * a) % c,理論上等價于 ans = (ans % c) * (a % c)但是不知道為什么這樣寫會出錯。已解決,因為可能最后一次相乘的時候返回一個未除盡的數

#相當于遍歷二進制的b

????????b >>= 1

# A(n) == A(n-1)^2,% c可以提高效率

????????a = (a % c) * (a % c)

????return ans

我們現在來看核心原理:

對于任何一個整數的模冪運算

a^b%c

對于b我們可以拆成二進制的形式 ?

b=b0+b1*2+b2*2^2+...+bn*2^n ?

這里我們的b0對應的是b二進制的第一位(倒數第一位),那么我們的a^b運算就可以拆解成

a^b0*a^b1*2*...*a^(bn*2^n) ?

對于b來說,二進制位不是0就是1,那么對于bx為0的項我們的計算結果是1就不用考慮了,我們真正想要的其實是b的非0二進制位,那么假設除去了b的0的二進制位之后我們得到的式子是

a^(bx*2^x)*...*a(bn*2^n) ?

這里我們再應用我們一開始提到的公式,那么我們的a^b%c運算就可以轉化為

(a^(bx*2^x)%c)*...*(a^(bn*2^n)%c)

這樣的話,我們就很接近快速冪的本質了。

(a^(bx*2^x)%c)*...*(a^(bn*2^n)%c)

我們會發現令

A1=(a^(bx*2^x)%c) ?

... ?

An=(a^(bn*2^n)%c) ?

這樣的話,假設bx都=1,An始終等于A(n-1)的平方,依次遞推。

首先,我們會觀察到,我們每次都是將b的規模縮小了2倍。

那么很顯然,原本的樸素的時間復雜度是O(n)。快速冪的時間復雜度就是O(logn)。在數據量越大的時候,者中優化效果越明顯。

[if !supportLists]九、[endif]Miller-Rabin素性測試算法


素性測試(即測試給定的數是否為素數)是近代密碼學中的一個非常重要的課題。雖然Wilson定理(對于給定的正整數n,n是素數的充要條件為)給出了一個數是素數的充要條件,但根據它來素性測試所需的計算量太大,無法實現對較大整數的測試。目前,盡管高效的確定性的素性算法尚未找到,但已有一些隨機算法可用于素性測試及大整數的因數分解。下面描述的Miller-Rabin素性測試算法就是一個這樣的算法。

算法:

首先要知道費馬定理只是n是素數的必要條件。即費馬定理不成立,n一定是合數;費馬定理成立,n可能是素數。接下來請看Miller-Rabin算法的分析過程。

x^2 = 1(mod p),p為質數,x小于p

x = 1或 p -1

x的偶數次方對p取余數,結果可能是1^x * (p-1)^y對p取余數,即結果有可能是1,p-1,或(p-1)^k對p取模,當k為偶數時=1,當k為奇數時=p-1

若有解,3/4概率是質數


算法實現:

# n為要檢驗的大質數,a < n,k = n - 1

def miller_rabin_witness(a, n):

????if n == 1:

????????return False

????if n == 2:

????????return True

# n - 1 = m * 2^q求解 m, q,因為n為偶數,所以必有解

????k = n - 1

# 2為 底數,n為N

????q = int(math.floor(math.log(k, 2)))

????while q > 0:

????????m = k / 2 ** q

#必須同時滿足兩個條件,因為m有可能是未除盡的數

????????if k % 2 ** q == 0 and m % 2 == 1:

????????????break

????????q = q - 1

#先計算 a ^ (n-1) == 1 mod(n) 是否成立,不成立必定為合數

????if quick_pow_mod(a, n - 1, n) != 1:

????????return False

#計算第一項

????b1 = quick_pow_mod(a, m, n)

????for i in range(1, q + 1):

????????if b1 == n - 1 or b1 == 1:

????????????return True

#后一項等于前一項的平方mod n

????????b2 = b1 ** 2 % n

????????b1 = b2

????if b1 == 1:

????????return True

????return False



# Miller-Rabin素性檢驗算法,檢驗8次

def prime_test_miller_rabin(p, k):

????while k > 0:

????????a = random.randint(1, p - 1)

????????if not miller_rabin_witness(a, p):

????????????return False

????????k = k - 1

????return True


十、[endif]擴展歐幾里德算法

擴展歐幾里德算法是用來在已知a, b求解一組x,y,使它們滿足貝祖等式: ax+by = gcd(a, b) =d(解一定存在,根據數論中的相關定理)。

e取65537,故list[0] * s + list[1] * e = 1,list[1]為(e)mod(s)的乘法逆元,也就是e對于φ(n)的模反元素d,此方程必有解。

歐幾里德算法停止的狀態是:a= gcd, b = 0 ,那么,這是否能給我們求解 x y 提供一種思路呢?因為,這時候,只要 a = gcd 的系數是 1 ,那么只要 b 的系數是 0 或者其他值(無所謂是多少,反正任何數乘以 0 都等于 0 但是a 的系數一定要是 1),這時,我們就會有: a*1 + b*0 = gcd

當然這是最終狀態,但是我們是否可以從最終狀態反推到最初的狀態呢?

假設當前我們要處理的是求出a和 b的最大公約數,并求出 x 和 y 使得 a*x + b*y= gcd ,而我們已經求出了下一個狀態:b 和 a%b 的最大公約數,并且求出了一組x1 和y1 使得: b*x1 + (a%b)*y1 = gcd , 那么這兩個相鄰的狀態之間是否存在一種關系呢?

我們知道:a%b = a - (a/b)*b(這里的 “/” 指的是整除,例如 5/2=2 , 1/3=0), 代入b*x1 + (a%b)*y1 = gcd 那么,我們可以進一步得到:

gcd = b*x1 + (a-(a/b)*b)*y1

= b*x1 + a*y1– (a/b)*b*y1

= a*y1 + b*(x1– a/b*y1)

對比之前我們的狀態:求一組x和 y 使得:a*x + b*y = gcd ,是否發現了什么?

這里:

x = y1

y = x1– a/b*y1

算法實現:

#這里的邏輯很復雜

#擴展歐幾里得算法,得到結果list[0]是a的系數,list[1]是b的系數 list[0] * a + list[1] * b = 1,但是有可能得到的list[1]是負數

def ex_euclid(a, b, list):

# b==0時 a 為先求出最大公約數

????if b == 0:

????????list[0] = 1L

????????list[1] = 0L

????????list[2] = a

????else:

#把b作為a傳入函數,會形成一個交替的過程以 8,3為例,以次為[8,3],[3,2],[2,1],[1,0],即函數入棧時,第二個參數的值為入棧后第一個參數的值

#對應著出棧時,函數的第一個參數的值等于出棧后第二個參數

# [8, 3], [3, 2], [2, 1], [1, 0]對應的list取值為[-1,3,1][1,-1,1][0,1,1][1,0,1]

????????ex_euclid(b, a % b, list)

????????temp = list[0]

????????# a%b = a - (a/b)*b ,b*x1 + (a%b)*y1 = gcd

# gcd = b*x1 + (a-(a/b)*b)*y1 = b*x1 + a*y1–(a/b)*b*y1 = a*y1 + b*(x1 – a/b*y1)

#故出棧時,函數的第二個參數的系數等于出棧后第一個參數的系數,出棧后第二個參數b的系數=x1 – a/b*y1

????????list[0] = list[1]

#算法 1

????????list[1] = temp - a / b * list[1]

# 3 * x1 + 2 * y1 = 1,x1已知= 1,y1 = (1 - 3 * x1 )/2

#算法2,結果一致,使用時注釋temp = list[0]

????????# list[1] = (list[2] - a * list[0]) / b



#求模反元素

def mod_inverse(a, b):

????# x = list[0],y = list[1],q = list[2]

????list = [0L, 0L, 0L]

????if a < b:

????????temp = a;a = b;b = temp;

????ex_euclid(a, b, list)

#改進,將負的模反元素變為正的模反元素,根據公式 ed ≡ 1 (mod (s)),ed + s * k ≡ 1 (mod (s)),k為任意整數,令 k = m * e,即e的整數倍

# e(d + s * m)≡ 1 (mod (s)), abs(b) < s,所以只加一次即可

#此處只對list[1]進行修改

????if list[1] < 0:

????????list[1] = a + list[1]

????return list[1]

RSA實現流程

1.先創建一個包含有接近一萬小質數的數組,隨機獲得一個30-31位數的十進制數字num,判斷是否與數組元素都互質,若不互質則+2,直到獲得一個都互質的整數

2.對num進行Miller-Rabin素性檢驗8次或者更多次。如果num沒有通過檢驗,重新隨機生成大整數重復之前步驟,否則認為num是素數。Miller-Rabin素性檢驗有一定概率會失敗。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容