引言:
CTF密碼學類題目中,RSA加密可謂是很重要且常見的加密類型,今天就總結下 RSA加密的原理及解密方法。
文章目錄
RSA算法簡介:
-
RSA加密算法是一種 非對稱加密 算法,RSA算法相比別的算法思路非常清晰,但是想要破解的難度非常大。
-
RSA算法基於一個非常簡單的數論事實:兩個素數相乘得到一個大數很容易,但是由一個大數分解為兩個素數相乘卻非常難。
1、什么是非對稱加密算法:
- 和 對稱加密 算法使用同一個密鑰進行加密解密的方式不同,非對稱加密 算法是使用不同密鑰進行加密和解密的算法,也稱為公私鑰加密。
非對稱加密算法實現機密信息交換的基本過程:
- 甲方生成 一對密鑰 並將其中的一把作為 公鑰 向其它方公開,得到該公鑰的乙方使用該密鑰對機密信息進行加密后再發送給甲方,甲方再用自己保存的另一把 私鑰 對加密后的信息進行解密。
圖解:

2、RSA 加密原理:
了解了非對稱加密算法的原理,我們再來說說 RSA 加密算法的基本流程。
如圖:
3、RSA加密算法過程詳解:
先來了解一下什么是 互質數:
- 兩個或多個整數的公因數只有1的非0自然數,則兩個非0自然數叫做互質數。例如 2 和 3,公因數只有1,所以為互質數。
1、找出質數 :
隨機找兩個質數 p 和 q ,p 與 q 越大,越安全。
2、計算公共模數:
n = p * q
假設 p = 65 ,q = 71。計算他們的乘積 n = p * q = 4615 ,轉化為二進制為 1001000000111,即該加密算法即為 13 位。位數越長,算法則越難被破解。實際應用中,RSA密鑰一般是1024位,重要場合則為2048位。
3、計算歐拉函數 φ(n):
φ(n) = φ(p*q) = (p-1)(q-1)
φ(n) 表示:在小於等於 n 的正整數之中,與 n 構成互質關系的數的個數。
例:
在 1 到 8 之中,與 8 形成互質關系的是1、3、5、7,所以 φ(n) = 4。
計算公式(即歐拉函數)為:
前提:P 與 Q 均為質數
φ(n) = φ(P*Q)= (P-1)(Q-1)
設:
P = 65 ,Q = 71
φ(n) = 64 * 70 = 4480
即有4480 個數與 n(4615)互質。
4、計算公鑰 e:
1 < e < φ(n)
要求:
- e 的取值必須是整數
- e 和 φ(n) 必須是互質數
5、計算私鑰 d:
計算公式:
e * d % m = 1 其中(φ(n) = m)
即找一個整數 d,使得 (e * d ) % m = 1,等價於 e * d - 1 = y * m ( y 為整數)。
得到 d ,實質就是對下面二元一次方程求解:
e * x - m * y = 1 e,m為已知量,求 x,y。
這個方程可以用 擴展歐幾里得算法 求解,此處就不詳細介紹了,可以看這個了解下: 擴展歐幾里得算法
得到:
-
公鑰=(e,n)
-
私鑰=(d,n)
對外,只暴露公鑰。
6、加密生成密文:
C = M e M^e Me mod n
C:密文 M:明文
7、解密生成明文:
M = C d C^d Cd mod n
C:密文 M:明文
下面舉一個完整的例子
4、示例:
1、找出質數 p 、q :
p = 3
q = 11
2、計算公共模數 n:
n = p * q = 3 * 11 = 33
n = 33
3、計算歐拉函數 φ(n):
φ(n) = (p-1)(q-1) = 2 * 10 = 20
φ(n) = 20
4、計算公鑰 e:
1 < e < φ(n)
1 < e < 20
e 的取值范圍為: { 3, 7, 9, 11, 13, 17, 19 }
為了方便測試,我們取最小的值 e =3,3 和 φ(n) = 20 互為質數,滿足條件。
5、計算私鑰 d:
e * d % φ(n) = 1
3 * d % 20 = 1
計算出 d = 7
6、公鑰加密:
公式:C = M e M^e Me mod n
隨便拿一個數字,這里方便演示 取 M = 2
C = 3 3 3^3 33 % 33 = 8
" M = 3 " 經過RSA加密后變成了 " C = 8 "
7、私鑰解密:
M = C d C^d Cd mod n
C = 8
d = 7
n = 33
計算:
M = 8 7 8^7 87 % 33
解得: M = 2
🆗,簡單了解 RSA的基本原理之后,來看幾個CTF中的RSA例題。
5、例題:
1、easy_RSA:

打開附件:

這題很簡單,就是求參數d 的值,那么如何計算d 呢?也就是如何求 e * x - m * y =1 式子中的 x ?
步驟:
1、先計算歐拉函數:

得到 φ(n) = 2135733082216268400
2、求 d:
根據加密原理,解密的方法就是:把歐拉函數的值 +1,再除以17 * n(這里 n 先取1)

得到 d = 125631357777427553,嘗試提交flag: cyberpeace{125631357777427553} 正確,看來 n 的值確實為 1 。( ̄▽ ̄)"
提供一個求私鑰腳本:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import gmpy2
p = 473398607161
q = 4511491
e = 17
m = (p-1)*(q-1)
d = int(gmpy2.invert(e,m)) //invert: 求逆元,返回值是一個 mpz 對象
print('私鑰為:',d)
運行結果(我的python出了點小問題):

得到 d = 125631357777427553
2、i春秋 RSA :

打開附件:

可以看到題目給出了 n,e,c 三個值 ,求明文的值。
首先先對 n 進行因式分解,使用在線網站:大數分解網站
得到 p ,q :

p = 310935513029228809998830208036655366162721470228774287453148308675193510132489142448801010943658159980501154153084396100667001391643762749806500051502679498536716532334917842894939889468693960937309663256592497965458780801192062835123429808
544757340971089756707788360038227894054989413747980167536893779923551227744017809301855984582408943622461942486239113822841696775958645014753081946441406022729616992302829930205076689399802050792392219242304302303180769915076199603301447453
07022538024878444458717587446601559546292026245318907293584609320115374632235270795633933755350928537598242214216674496409625928797450473
q = 310935513029228809998830208036655366162721470228774287453148308675193510132489142448801010943658159980501154153084396100667001391643762749806500051502679498536716532334917842894939889468693960937309663256592497965458780801192062835123429808
544757340971089756707788360038227894054989413747980167536893779923551227744017809301855984582408943622461942486239113822841696775958645014753081946441406022729616992302829930205076689399802050792392219242304302303180769915076199603301447453
07022538024878444458717587446601559546292026245318907293584609320115374632235270795633933755350928537598242214216674496409625928997877221
2、相關參數都已得到,就可以編寫腳本求明文信息了:
#!/usr/bin/env python
# coding:utf-8
import gmpy2
p = gmpy2.mpz(31093551302922880999883020803665536616272147022877428745314830867519351013248914244880101094365815998050115415308439610066700139164376274980650005150267949853671653233491784289493988946869396093730966325659249796545878080119206283512342980854475734097108975670778836003822789405498941374798016753689377992355122774401780930185598458240894362246194248623911382284169677595864501475308194644140602272961699230282993020507668939980205079239221924230430230318076991507619960330144745307022538024878444458717587446601559546292026245318907293584609320115374632235270795633933755350928537598242214216674496409625928997877221)
q = gmpy2.mpz(31093551302922880999883020803665536616272147022877428745314830867519351013248914244880101094365815998050115415308439610066700139164376274980650005150267949853671653233491784289493988946869396093730966325659249796545878080119206283512342980854475734097108975670778836003822789405498941374798016753689377992355122774401780930185598458240894362246194248623911382284169677595864501475308194644140602272961699230282993020507668939980205079239221924230430230318076991507619960330144745307022538024878444458717587446601559546292026245318907293584609320115374632235270795633933755350928537598242214216674496409625928797450473)
e = gmpy2.mpz(65537)
#計算私鑰d
m = (p - 1) * (q - 1)
d = gmpy2.invert(e, m)
print "private key:",d
#求密文M
c = gmpy2.mpz(168502910088858295634315070244377409556567637139736308082186369003227771936407321783557795624279162162305200436446903976385948677897665466290852769877562167487142385308027341639816401055081820497002018908896202860342391029082581621987305533097386652183849657065952062433988387640990383623264405525144003500286531262674315900537001845043225363148359766771033899680111076181672797077410584747509581932045540801777738548872747597899965366950827505529432483779821158152928899947837196391555666165486441878183288008753561108995715961920472927844877569855940505148843530998878113722830427807926679324241141182238903567682042410145345551889442158895157875798990903715105782682083886461661307063583447696168828687126956147955886493383805513557604179029050981678755054945607866353195793654108403939242723861651919152369923904002966873994811826391080318146260416978499377182540684409790357257490816203138499369634490897553227763563553981246891677613446390134477832143175248992161641698011195968792105201847976082322786623390242470226740685822218140263182024226228692159380557661591633072091945077334191987860262448385123599459647228562137369178069072804498049463136233856337817385977990145571042231795332995523988174895432819872832170029690848)
print "明文:"
M = pow(c,d,p*q)
print '10進制: '+str(M)
flag = str(hex(M))[2:]# 從第3位為往后截取
print '16進制: '+flag
print 'ASCII碼: '+flag.decode('hex')
注釋:
- gmpy2.mpz(x): 初始化一個大整數x
- pow( x, y, z): 計算 x y x^y xy mod z
得到 flag:

🆗,關與RSA的總結暫時就到這里了,以后發現好用的方法或解題技巧會接着更新。╰( ̄ω ̄o)
