一文搞懂RSA算法原理及简单实现
前言
RSA算法是最重要的算法之一,它是一種非對(duì)稱加密,是目前最有影響力的加密方式之一。這篇文章我們通過實(shí)現(xiàn)一種簡(jiǎn)單的RSA加密來探究它的原理。
計(jì)算公鑰和私鑰
RSA中的公鑰和私鑰需要結(jié)合在一起工作。公鑰用來對(duì)數(shù)據(jù)塊加密,之后 ,只有對(duì)應(yīng)的私鑰才能用來解密。生成密鑰時(shí),需要遵循幾個(gè)步驟以確保公鑰和私鑰的這種關(guān)系能夠正常工作。這些步驟也確保沒有實(shí)際方法能夠從一個(gè)密鑰推出另一個(gè)。
開始前,首先要選擇兩個(gè)大的素?cái)?shù),記為p和q。根據(jù)當(dāng)今求解大數(shù)因子的技術(shù)水平,這兩個(gè)數(shù)應(yīng)該至少有200位,這們?cè)趯?shí)踐中才可以認(rèn)為是安全的。
然后,開始計(jì)算n:
n = pq
接下來,選擇一個(gè)小的奇數(shù)e,它將成為公鑰的一部分。選擇e最需要考慮的重點(diǎn)是它與(p-1)(q-1)不能有相同的因子。換句話說,e與(p-1)(q-1)是互為素?cái)?shù)關(guān)系的。比如,如果p=11而q=19,那么n=11 X 19=209。這里選擇e=17,因?yàn)?p-1)(q-1)=10 X 18 =180,而17和180沒有相同的因子。通常選擇3、17、65、537作為e的值。使用這些值不會(huì)對(duì)RSA的安全性造成影響,因?yàn)榻饷軘?shù)據(jù)還需要用到私鑰。
一旦為e選擇了一個(gè)值,接下來開始計(jì)算相對(duì)應(yīng)的值d,d將成為私鑰的一部分。d的值就是計(jì)算e的倒數(shù)對(duì)(p-1)(q-1)的取模結(jié)果,公式如下:
d = e-1 mod (p-1)(q-1)
這里d和e是模乘法逆元的關(guān)系。
思考一下這個(gè)問題:當(dāng)d為多少時(shí)可以滿足ed mod (p-1)(q-1) = 1 ?比如在等式 17d mod 180 = 1中,d的一個(gè)可能值是53。其他的可能值是233、413、593等。在實(shí)踐中,可以利用歐幾里德算法來計(jì)算模乘法逆元。這里就不再展開。
現(xiàn)在有了e和d的值,將(e,n)作為公鑰P,將(d,n)作為私鑰S并保持其不可見。
如何計(jì)算d?
上面p、q、e需要預(yù)設(shè)三個(gè)素?cái)?shù),n很容易求出來,但是d的計(jì)算就涉及到模的運(yùn)算了
什么是模、取模和模運(yùn)算?
取模:
https://baike.baidu.com/item/%E5%8F%96%E6%A8%A1%E8%BF%90%E7%AE%97/10739384?fr=aladdin
模運(yùn)算:
https://baike.baidu.com/item/%E6%A8%A1%E8%BF%90%E7%AE%97/4376110
具體就不細(xì)說了,但是要注意取模和取余的區(qū)別
這里d = e-1 mod (p-1)(q-1)
簡(jiǎn)化為:
d = e-1 % m
這是乘法逆元的問題。我們對(duì)上面的進(jìn)行處理
d * e = e-1 % m * e
(d * e) % m = (e-1 % m * e) %m
根據(jù)模運(yùn)算的結(jié)合率
(a%p * b)%p=(a * b)%p
(d * e) % m = (e-1 % m * e) %m = (e-1 * e) % m = 1 % m
所以我們最后得到
(d * e) % m = 1 % m
這里由于n說我們自己定義的,一定是正數(shù),所以1%n=1
所以最后變?yōu)橛?jì)算
(d * e) % m = 1
并且e和d一定有一組解滿足他們都小于m。我們只需求這組解即可。
根據(jù)費(fèi)馬小定理,如果a和b互質(zhì),則
ab-1 % b = 1
那么已經(jīng)要求e與m互質(zhì),所以
(e * em-2) % m = 1
所以d的一個(gè)解是em-2,但是這個(gè)很可能比m大,則可以表示為m + k,那么
(e * (m + k)) % m = 1
根據(jù)模的加法運(yùn)算規(guī)則
(em % m + e*k % m) % m = 1
因?yàn)閑m % m一定是0,所以上面的可以轉(zhuǎn)為
e*k % m = 1
如果k還大于m,則重復(fù)上面的步驟直到k小于m。這時(shí)k就是d。
因?yàn)閑小于m,所以d一定有一個(gè)小于m的解使 (d * e) % m = 1成立
代碼
簡(jiǎn)單的算法是遍歷找到d,代碼:
var q = 13; var p = 17; var n = q * p; var e = 7; var tmp = (q - 1) * (p - 1); var d; for (d = 1; ; d++) {if (e * d % tmp === 1) {break} }這里還需要進(jìn)行優(yōu)化,因?yàn)橐话鉵都是超大數(shù),而e則比較小,所以d也會(huì)很大,這里就需要大量的循環(huán),優(yōu)化后如下:
var d;for (var j = 1; j < e; j++) {if ((tmp * j + 1) % e === 0) {d = (tmp * j + 1) / e;break;}}因?yàn)?(d * e) % m = 1也就是
d * e = k * m + 1
而且d也需要小于m,所以k一定小于e,而e是比較小的值,所以我們將循環(huán)改成k即可減少大量的計(jì)算。
加密和解密數(shù)據(jù)分組
要使用RSA算法對(duì)數(shù)據(jù)進(jìn)行加密和解密,首先要確定分組的大小。為了實(shí)現(xiàn)這一步,必須確保該分組可以保存的最大數(shù)值要小于n的位數(shù)。比如,如果p和q都是200位數(shù)字的素?cái)?shù),則n的結(jié)果將小于400位。因而,所選擇的分組所能保存的最大值應(yīng)該要以是接近于400。在實(shí)踐中,通常選擇的位數(shù)都是比n小的2的整數(shù)次冪。比如,如果n是209,要選擇的分組大小就是7位,因?yàn)?7 = 128比209小,但28 = 256又大于209。
要從緩沖區(qū)M中加密第(i)組明文Mi ,使用公鑰(e,n)來獲取M的數(shù)值,對(duì)其求e次冪,然后再對(duì)n取模。這將產(chǎn)生一組密文Ci。對(duì)n的取模操作確保了Ci將和明文的分組大小保持一致。因而,要加密明文分組有:
Ci = Mie mod n
之前提到過,歐拉函數(shù)是采用冪模運(yùn)算來加密數(shù)據(jù)的基礎(chǔ),根據(jù)歐拉函數(shù)及其推導(dǎo)式,能夠?qū)⒚芪慕饷芑卦摹?/p>
要對(duì)緩沖區(qū)中C中的第(i)組密文進(jìn)行解密,使用私鑰(d,n)來獲取Ci的數(shù)值部分,對(duì)其求d次冪,然后再對(duì)n取模。這將產(chǎn)生一組明文Mi。因此,要解密密文分組有:
Mi = Cid mod n
計(jì)算過程及優(yōu)化
這里加密解密的算法一樣,只不過key值不同而已,涉及的是模的冪運(yùn)算
以加密為例
Ci = Mie mod n
因?yàn)樾枰紤]大數(shù)的問題,所以模的冪運(yùn)算不能直接運(yùn)算,比如如果我先直接計(jì)算Mie,由于Mi有可能是很大的數(shù),這樣它的e次冪就會(huì)是一個(gè)超級(jí)數(shù)字,計(jì)算機(jī)無法計(jì)算和存儲(chǔ)
所以這里我們就需要對(duì)模冪運(yùn)算進(jìn)行優(yōu)化,就涉及到了蒙哥馬利算法
參考
https://blog.csdn.net/zgzczzw/article/details/52712980
https://blog.csdn.net/linraise/article/details/17490769)
蒙哥馬利算法比較復(fù)雜,包含三個(gè)算法進(jìn)行優(yōu)化。
我們先設(shè)計(jì)一個(gè)簡(jiǎn)單的算法,先將模冪運(yùn)算轉(zhuǎn)化為模乘運(yùn)算
關(guān)于模運(yùn)算,有如下幾個(gè)公式:
結(jié)合律(a%p*b)%p=(a*b)%p同理((a*b) % p * c)% p = (a*b*c) % p 四則運(yùn)算(a * b) % p = (a % p * b % p) % p (a^b) % p = ((a % p)^b) % p我們先利用上面兩個(gè)結(jié)合律,所以:
a2%n = (a * a) %n = (a%n * a) %n
因?yàn)閍%n取模一定比a小,所以a%n*a就要比a2小很多,類推
a3%n = (a2 * a) %n = ((a2%n)*a)%n
得出
an%n = ((an-1%n)*a)%n
這是一個(gè)簡(jiǎn)單遞歸算法,通過這個(gè)算法,每次乘完都會(huì)做一個(gè)取模運(yùn)算,運(yùn)算的數(shù)據(jù)就會(huì)小很多。
代碼:
function encode(x, e, n) {var result = x % n;for (var i = 1; i < e ; i++) {result = (result * x) % n;}return result }function decode(x, d, n) {var result = x % n;for (var i = 1; i < d ; i++) {result = (result * x) % n;}return result }當(dāng)然,上面僅僅是簡(jiǎn)單例子,因?yàn)槿绻麅鐢?shù)較大比如d就會(huì)是一個(gè)超大數(shù),這樣循環(huán)次數(shù)就會(huì)很多,計(jì)算時(shí)間很長(zhǎng)。
根據(jù)模的運(yùn)算法則:
(a % p * b) % p = (a * b) % p
(a * b) % p = (a % p * b % p) % p
我們可以得出,當(dāng)指數(shù)(假設(shè)為e)是偶數(shù)時(shí)
se % n = ((se/2 % n) * (se/2 % n)) % n
當(dāng)為奇數(shù)時(shí)則可以先轉(zhuǎn)成偶數(shù)
se % n = ((se - 1 % n) * e) % n
這樣就可以用二分法和位運(yùn)算來優(yōu)化算法。如下:
function modpow(x, p, m) {if(p === 1){return mod(x, m)}var mid;if((p & 1) === 0){mid = (p >> 1);var tmp1 = modpow(x, mid, m);return mod(tmp1 * tmp1, m);}else{return mod(modpow(x, p - 1, m) * x, m)} }1、利用遞歸二分。因?yàn)殚_方所以每個(gè)節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都相等,所以計(jì)算其中一個(gè)就可以,這樣我們只需計(jì)算二叉樹的一條路徑就可以了,整體復(fù)雜度只有O(log2n)。比如2n只需要計(jì)算n + x次(最多壞情況每次都是奇數(shù)則是2n),比2n次計(jì)算節(jié)省大量的時(shí)間,而且數(shù)據(jù)越大節(jié)省時(shí)間越多。
2、位運(yùn)算。在判斷奇偶數(shù)時(shí),沒有使用除法,因?yàn)槌ㄟ\(yùn)算復(fù)雜度很大,耗時(shí)比其他運(yùn)算長(zhǎng)很多。這里使用位運(yùn)算,只需判斷最低位是否為0即可,而除2運(yùn)算則可以用右移一位代替。因?yàn)橛?jì)算機(jī)中位運(yùn)算最快,所以這樣會(huì)節(jié)省大量的時(shí)間。
正確性驗(yàn)證
加密我們可以理解,因?yàn)檫\(yùn)算中有模參與,所以不可逆。但是加密后為什么通過私鑰就可以解密,解密一定正確么?
首先加密
k = se % n
然后對(duì)k解密
r = ((se % n)d) %n
根據(jù)(a^b) % p = ((a % p)^b) % p可得
r = (se)d % n = se*d % n
通過之前的計(jì)算可知 (d * e) % m = 1,而m是(q-1)(p-1),所以
d * e = k(q-1)(p-1) + 1 (k未知)
而且n=pq,所以
r = sk(q-1)(p-1) + 1 % (pq)
根據(jù)費(fèi)馬小定理,如果a和b互質(zhì),則
ab-1 = 1 mod b
所以考慮兩種情況:
1、s與n即pq互質(zhì)
因?yàn)閜和q是兩個(gè)大質(zhì)數(shù),所以s與n互質(zhì)就相當(dāng)于s分別于p和q互質(zhì)
所以根據(jù)費(fèi)馬小定理
sp-1 = 1 mod p
即
sp-1 % p = 1
所以根據(jù)冪模的運(yùn)算法則(a^b) % p = ((a % p)^b) % p
sk(q-1)(p-1) % p = (sp-1 % p)k(q-1) % p
因?yàn)閟p-1 % p = 1,所以
sk(q-1)(p-1) % p = 1k(q-1) % p = 1
同理可以得出
sk(q-1)(p-1) % q = 1
所以sk(q-1)(p-1) - 1可以被p和q都整除,得出
sk(q-1)(p-1) % (pq) = 1
回到之前
r = sk(q-1)(p-1) + 1 % (pq) = (sk(q-1)(p-1) * s) % (pq)
根據(jù)模的結(jié)合率(a%p * b)%p=(a * b)%p
r = ( (sk(q-1)(p-1) % (pq)) * s ) % (pq)
上面推出sk(q-1)(p-1) % (pq) = 1,所以
r = s % (pq)
因?yàn)閜q = n所以最終
r = s % n
根據(jù)上面RSA算法要求,可知s一定是小于n的數(shù),所以s對(duì)n取模結(jié)果也是s
所以 r = s 驗(yàn)證了RSA的正確性。
2、s于n不互質(zhì)
總結(jié)
以上是生活随笔為你收集整理的一文搞懂RSA算法原理及简单实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅析在公众号中使用弛声sdk为什么上传解
- 下一篇: 如何实现一套可切换的声网+阿里的直播引擎