前言:最近好久沒更新博客,因為在忙着考雅思還有一直也在看CTF(=_=上個星期玩了一下騰訊的CTF。。。。這酸爽,我一直都認為比賽是最容易提升自己實力的一種途徑,上次比賽也發現了自己的不足,繼續努力吧少年)今天看了一天的RSA算法。以前在上課的時候,記得老師光講這個算法就講了有兩堂課。現在在看一下其實也不是很難(別騙自己了=_=)所以寫這篇文章記錄一下,順便加深一下自己的印象
0x1
首先開始之前我們要補充一下關於RSA的一些常識
(1) 選擇一對不同的、足夠大的素數p,q。
(2)計算n=pq。
(3)計算f(n)=(p-1)(q-1),同時對p, q嚴加保密,不讓任何人知道。
(4)找一個與f(n)互質的數e,且1<e<f(n)。
(5)計算d,使得de≡1 mod f(n)。這個公式也可以表達為(d*e-1)% f(n)=0
這里要解釋一下,≡是數論中表示同余的符號。公式中,≡符號的左邊必須和符號右邊同余,也就是兩邊模運算結果相同。
顯而易見,不管f(n)取什么值,符號 右邊1 mod f(n)的結果都等於1;符號的左邊d與e的乘積做模運算后的結果也必須等於1。
這就需要計算出d的值,讓這個同余等式能夠成立。
(6)公鑰KU=(e,n),私鑰KR=(d,n)。
(7)加密時,先將明文變換成0至n-1的一個整數M。若明文較長,可先分割成適當的組,然后再進行交換。設密文為C,
則加密過程為:C≡M^e(mod n)。
(8)解密過程為:M≡C^d(mod n)。
同余:數論中的重要概念。給定一個正整數m,如果兩個整數a和b滿足(a-b)能夠被m整除,即(a-b)/m得到一個整數,那么就稱整數a與b對模m同余,
記作a≡b(mod m)。對模m同余是整數的一個等價關系
有了上面的RSA基礎可以知道RSA中的
p: 第一個大素數
q: 第二個大素數
模數n: n = p*q
f(n): (p-1)*(q-1)
公鑰指數e: 與 f(n)互質, 且 1 < e < f(n)
私鑰指數d: 滿足e * d ≡ 1 (mod f(n))
公鑰 = {n, e},一般公開
私鑰 = {d, e}
0x2
就拿實驗吧上面的一個題做這個例子吧因為最近也一直在玩CTF
http://ctf5.shiyanbar.com/crypto/RSAROLL.txt
第一行我們看到了這樣一組數,直覺告訴我們這個很有可能是公鑰(當然也不排除出題師傅直接把私鑰給你,這樣出題師傅是不是就太可愛了呀)
{920139713,19}
根據我們上面補充的知識知道要按照步驟來獲取私鑰
(1) n = 920139713 e =19
(2) 這時候我們要將整數n分解為兩個素數,我們可以使用在線網站直接來獲取
http://www.factordb.com/index.php
當然我們可以寫代碼來實現
如下:def divde(n):
start =2
while start < math.sqrt(n):
if (n%start==0):
record = start
print(“大整數可以分解為:”,str(start),str(int(n/base)))
break
base+=1
return (start,int(n/base))
(3) 經過上面的操作我們可以獲得
p = 18443,q = 49891,f(n) = (p-1)*(q-1)/p,q 是上n分解的兩個質數,f(n)是歐拉函數
(4) 根據我們現在已經獲得的條件以及前面補充的RSA知識我們知道只要找到一個滿足e * d ≡ 1 (mod f(n)) 這個方程式的d,我們就成功獲取的私鑰(於是我就自己寫了一個函數跑了一下,跑了好長時間仍然沒有結果,在我快要放棄的時候我忽然想到了gmpy2,其中有一個神奇的函數)
(g,d,_) = gcdext(e,f(n)) //不知道為什么是這種格式但是它的效果是與e * d ≡ 1 (mod f(n)) 使用此函數只用了不到一秒的時間就獲得了私鑰d= 96849619
(5) 至此成功拿到了私鑰,剩下的就剩解密密文
利用pow(i,d,n))解密密文 //這里的i就是密文,d是我們剛才獲取的私鑰,n公鑰如下圖一
圖一獲取的是ASCII
(6) 最后將ASCII轉化為相應的字符(這里我用了自己寫的一個小腳本工具)
0x3代碼部分
#-*- coding:utf-8 -*-
from gmpy2 import *
from Crypto.Util.number import *
利用gmpy2中的gcdext()函數 實現的功能是 (d*e-1)% f(n)=0從而獲取私鑰d
def decrypt():
flag = ''
n = 920139713
e =19
p = 18443
q = 49891
phi_n = (p-1)*(q-1)
(g,d,_) = gcdext(e,phi_n) #利用此函數可以獲取私鑰d值
print("Privcate key:",str(g),str(d))
plaintext =""" 704796792
752211152
274704164
18414022
368270835
483295235
263072905
459788476
483295235
459788476
663551792
475206804
459788476
428313374
475206804
459788476
425392137
704796792
458265677
341524652
483295235
534149509
425392137
428313374
425392137
341524652
458265677
263072905
483295235
828509797
341524652
425392137
475206804
428313374
483295235
475206804
459788476
306220148
"""
plaintext = plaintext.split()
for i in plaintext:
#利用pow(l,d,n) l的d次方然后取n的余數
i = int(i)
print((pow(i,d,n)),end=' ')
if __name__=='__main__':
decrypt()
0x4 后記
嘻嘻,前段時間女朋友鬧着分手,上個星期她又打電話個更說對不起,我們重新和好吧。嗯~ o(* ̄▽ ̄*)o知錯就改還是好同志嘛!之前好傷心呢,現在一起都過去了。我會以更加昂揚的斗志生活。后后記+_+ pwn好難呀,不會呀!要多多看看這方面的文章。