1. 什么是RSA
1977年,麻省理工學院的 Ron Rivest、Adi Shamir 和 Leonard Adleman 共同提出了一種非對稱加密算法,用他們三人的姓氏縮寫命名為 RSA。RSA 既不是惟一,也不是最早的非對稱加密算法。但它是使用最廣泛,因而也是最重要的非對稱加密算法。
2. RSA算法描述
2.1 產生公私密鑰對
1.隨機選擇兩個不相等的質數p和q。
2.計算p和q的乘積n(n=p*q),n的長度就是密鑰長度。
3.計算n的歐拉函數φ(n): φ(n) = (p-1)(q-1)
4.隨機選擇一個整數e,也就是公鑰當中用來加密的那個數字 條件是1< e < φ(n),且e與φ(n) 互質。
5.取e的模反數d,計算方法為:e * d ≡ 1 (mod φ)
6.將n和e封裝成公鑰,n和d封裝成私鑰,(n,e),(n,d)就是密鑰對。
補充說明:
1.下面代碼中使用phi
代替歐拉函數φ(n)
2.如果兩個正整數e和n互質,那么一定可以找到整數d,使得 e * d - 1 被n整除,或者說e * d被n除的余數是1。這時,d就叫做e的“模反元素”。
2.2 RSA加密
首先對明文進行比特串分組,使得每個分組對應的十進制數小於n,然后依次對每個分組m做一次加密,所有分組的密文構成的序列就是原始消息的加密結果,即m滿足0<=m<n,則加密算法為:
c≡ m^e mod n
; c為密文,且0<=c<n。 (還可以表示為c = pow(m, e, N)
也就是說RSA加密是對明文的E次方后除以N后求余數的過程。只要知道E和N就可以進行RSA加密了,所以說E、N是RSA加密的密鑰,也就是說E和N的組合就是公鑰。即公鑰=(E,N)
2.3 RSA解密
對於密文0<=c<n,解密算法為: m≡ c^d mod n
;(還可以表示為m = pow(c, d, N))
也就是說對密文進行D次方后除以N的余數就是明文,這就是RSA解密過程。知道D和N就能進行解密密文了,所以D和N的組合就是私鑰。即私鑰=(D,N)
2.4 RSA相關值
值 | 含義 |
---|---|
p 和 q | 兩個不相等的質數 |
n | 大整數n,稱之為模數 |
e 和 d | 互為模反數的兩個指數 |
c 和 m | 分別是密文和明文 |
3. 安裝gmpy2
檢查一下是否安裝了wheel文件包,在cmd中輸入wheel
,查看一下
如果沒有安裝,則輸入pip install wheel
安裝
安裝好wheel后,還需要再安裝gmpy2所需要的whl文件,查找需要的whl文件包。注意,whl文件包需要和你所安裝的python3版本一致;
輸入python查看電腦Python版本所支持的whl文件版本
我使用的是python3.8 32位,下載gmpy2‑2.0.8‑cp38‑cp38‑win32.whl,放到python文件目錄下。
然后再在cmd中輸入:pip install (whl文件的路徑)
最后輸入:pip install gmpy2
就可以安裝,沒有報錯即安裝成功
4. 實戰練習
4.1 已知p、q、e,求d
[BUUCTF]RSA
編寫腳本
import gmpy2
p = 473398607161
q = 4511491
e = 17
d = gmpy2.invert(e,(p-1)*(q-1))
print(d)
運行得到
4.2.1 已知p、q、e、密文c,求明文m
[BUUCTF] rsarsa
打開文檔,給出了p、q、e的值
方法一:使用RSAtool工具
使用rsatool工具計算d的值,直接填入p,q,把e = 65537轉換為16進制在再填入,再點擊Calc.D,即可獲得D的值。
然后編寫腳本
p = 9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297777160200625281665378483
q = 11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732443992230647903887510065547947313543299303261986053486569407
C = 83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605647316646576486526217457006376842280869728581726746401583705899941768214138742259689334840735633553053887641847651173776251820293087212885670180367406807406765923638973161375817392737747832762751690104423869019034
d = 56632047571190660567520341028861194862411428416862507034762587229995138605649836960220619903456392752115943299335385163216233744624623848874235303309636393446736347238627793022725260986466957974753004129210680401432377444984195145009801967391196615524488853620232925992387563270746297909112117451398527453977
n = p*q
flag = pow(C,d,n)
print(flag)
得到明文flag
方法二:利用gmpy2
直接寫腳本
import gmpy2
p = 9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297777160200625281665378483
q = 11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732443992230647903887510065547947313543299303261986053486569407
e = 65537
C = 83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605647316646576486526217457006376842280869728581726746401583705899941768214138742259689334840735633553053887641847651173776251820293087212885670180367406807406765923638973161375817392737747832762751690104423869019034
#1.求d
d = gmpy2.invert(e,(p-1)*(q-1))
#2.求n
n = p*q
#3.m=pow(c,d,n)
flag = pow(C,d,n)
print(flag)
得到明文flag
4.2.2 已知c、q、n、e,求明文m
參考文章
給了 c, q, n ,e
求 p : n//q
求 d: d = invmod(e,(p-1)*(q-1))
求 flag : flag = pow(c,d,n)
import libnum
n = 191051885543358947736760989661967468461742175898801910645529003886391047898839624568290216360845330501814019720570327197669064365268607597117598905046895097642708006373182989953758208523010345148200475257538336602695211819055893667974317905617522838840325499754862033348148407978527792816186094297381925119601464149
q = 166836705584681518148179737955842605213272207836752187845124149461151181903779374775281529346854786259719545699157508885500818994019618158708212777833768444327658647324555090459233657737950932895018766440119999513331707759691054888319029069397903003240927552065429412176600134636921146805408664505115889561043
c = 177177672061025662936587345347268313127241651965256882323180749317515733256088163186914550682635245294414879862810654773207644687262596440870094409378849307188485755700138797651039936445998433830516207630858733090581643592843521203499818069822504434370840254518614785953412492701730326524258672860416318501278155194
e = 0x10001
p = n//q
print(p)
d = libnum.invmod(e,(p-1)*(q-1))
print(d)
i = pow(c,d,n)
print(libnum.n2s(i))
4.3 已知n、e、密文c,求明文m
[ctfshow]easyrsa1
分解質因數n
編寫腳本
import gmpy2
import binascii
e = 65537
n = 1455925529734358105461406532259911790807347616464991065301847
c = 69380371057914246192606760686152233225659503366319332065009
#1.將n分解為p和q
p = 1201147059438530786835365194567
q = 1212112637077862917192191913841
phi = (p-1)*(q-1)
#2.求d
d = gmpy2.invert(e,phi)
#3.m=pow(c,d,n)
m = gmpy2.powmod(c,d,n)
print(binascii.unhexlify(hex(m)[2:]))
#binascii.unhexlify(hexstr):從十六進制字符串hexstr返回二進制數據
運行腳本
注:
1.在線分解質因數:http://www.factordb.com
2.代碼中涉及到了python之binascii模塊相關知識
4.4 已知public key、密文c,求明文m
[BUUCTF]RSA
下載附件
用記事本打開pub.key
解析公鑰
模數和指數即為n和e,還需要把n從十六進制轉換為十進制
分解n,得到p和q
接下來就是通過腳本求出明文m
腳本需要用到python的rsa庫,在cmd中輸入pip install rsa
即可完成安裝
腳本
import gmpy2
import rsa
e=65537
n=86934482296048119190666062003494800588905656017203025617216654058378322103517
p=285960468890451637935629440372639283459
q=304008741604601924494328155975272418463
phin = (p-1) * (q-1)
d=gmpy2.invert(e, phin)
key=rsa.PrivateKey(n,e,int(d),p,q)
with open("flag.enc","rb") as f:
f=f.read()
print(rsa.decrypt(f,key))
運行腳本,得到flag。
4.5 已知p、q、dp、dq、c求明文m
[BUUCTF]RSA1
這道題屬於已知p、q、dp、dq、c求明文類型
上腳本
p = 8637633767257008567099653486541091171320491509433615447539162437911244175885667806398411790524083553445158113502227745206205327690939504032994699902053229
q = 12640674973996472769176047937170883420927050821480010581593137135372473880595613737337630629752577346147039284030082593490776630572584959954205336880228469
dp = 6500795702216834621109042351193261530650043841056252930930949663358625016881832840728066026150264693076109354874099841380454881716097778307268116910582929
dq = 783472263673553449019532580386470672380574033551303889137911760438881683674556098098256795673512201963002175438762767516968043599582527539160811120550041
c = 24722305403887382073567316467649080662631552905960229399079107995602154418176056335800638887527614164073530437657085079676157350205351945222989351316076486573599576041978339872265925062764318536089007310270278526159678937431903862892400747915525118983959970607934142974736675784325993445942031372107342103852
import gmpy2
I = gmpy2.invert(q,p)
mp = pow(c,dp,p)
mq = pow(c,dq,q) #求冪取模運算
m = (((mp-mq)*I)%p)*q+mq #求明文公式
print(hex(m)) #轉為十六進制
運行腳本
然后將16進制轉換成字符串
4.6已知n、e、dp、c,求m
[BUUCTF]RSA2
下載附件,題目給出公鑰n,e以及dp
dp=d%(p-1)
腳本
import gmpy2
import rsa
import binascii
p=0
e=65537
c = 140423670976252696807533673586209400575664282100684119784203527124521188996403826597436883766041879067494280957410201958935737360380801845453829293997433414188838725751796261702622028587211560353362847191060306578510511380965162133472698713063592621028959167072781482562673683090590521214218071160287665180751
dp=905074498052346904643025132879518330691925174573054004621877253318682675055421970943552016695528560364834446303196939207056642927148093290374440210503657
n=248254007851526241177721526698901802985832766176221609612258877371620580060433101538328030305219918697643619814200930679612109885533801335348445023751670478437073055544724280684733298051599167660303645183146161497485358633681492129668802402065797789905550489547645118787266601929429724133167768465309665906113
temp=dp*e
for i in range(1,e) :
if (temp-1)%i==0:
x=(temp-1)//i+1
y=n%x
if y==0:
p=x
break
q=n//p
#'//'代表向下取整,'/'得到的是浮點數
d=gmpy2.invert(e,(p-1)*(q-1))
key=rsa.PrivateKey(n,e,d,p,q)
m=pow(c,d,n)
print(binascii.unhexlify(hex(m)[2:]))
#unhexlify()的作用是返回16進制數對應的字符串
運行結果
4.7共模攻擊
[BUUCTF]RSA3
出現兩個加密使用相同的模,可以在不知道私鑰的情況下得到明文,這就是RSA中的共模攻擊。
腳本
from gmpy2 import invert
# 歐幾里得算法
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def main():
n = 22708078815885011462462049064339185898712439277226831073457888403129378547350292420267016551819052430779004755846649044001024141485283286483130702616057274698473611149508798869706347501931583117632710700787228016480127677393649929530416598686027354216422565934459015161927613607902831542857977859612596282353679327773303727004407262197231586324599181983572622404590354084541788062262164510140605868122410388090174420147752408554129789760902300898046273909007852818474030770699647647363015102118956737673941354217692696044969695308506436573142565573487583507037356944848039864382339216266670673567488871508925311154801
c1 = 22322035275663237041646893770451933509324701913484303338076210603542612758956262869640822486470121149424485571361007421293675516338822195280313794991136048140918842471219840263536338886250492682739436410013436651161720725855484866690084788721349555662019879081501113222996123305533009325964377798892703161521852805956811219563883312896330156298621674684353919547558127920925706842808914762199011054955816534977675267395009575347820387073483928425066536361482774892370969520740304287456555508933372782327506569010772537497541764311429052216291198932092617792645253901478910801592878203564861118912045464959832566051361
c2 = 18702010045187015556548691642394982835669262147230212731309938675226458555210425972429418449273410535387985931036711854265623905066805665751803269106880746769003478900791099590239513925449748814075904017471585572848473556490565450062664706449128415834787961947266259789785962922238701134079720414228414066193071495304612341052987455615930023536823801499269773357186087452747500840640419365011554421183037505653461286732740983702740822671148045619497667184586123657285604061875653909567822328914065337797733444640351518775487649819978262363617265797982843179630888729407238496650987720428708217115257989007867331698397
e1 = 11187289
e2 = 9647291
s = egcd(e1, e2)
s1 = s[1]
s2 = s[2]
# 求模反元素
if s1<0:
s1 = - s1
c1 = invert(c1, n)
elif s2<0:
s2 = - s2
c2 = invert(c2, n)
m = pow(c1,s1,n)*pow(c2,s2,n) % n
print(m)
if __name__ == '__main__':
main()
運行腳本,得到明文m
轉16進制
666c61677b34396439313037376131616263623134663161396435343663383062653965667d
十六進制轉字符串
看到一份來自漏斗社區的RSA解題思路思維導圖,收藏起來8。