1.質數(素數)是指在大於1的自然數中,除了1和它本身以外不再有其他因數的自然數。
2.合數是指比1大但不是素數的數
3.約數(因數)整數a除以整數b(b≠0) 除得的商正好是整數而沒有余數,我們就說a能被b整除,或b能整除a。a稱為 b的倍數,b稱為a的約數
4.互質數:如果兩個整數a,b的最大公因數(greatest common divisor)為1,即gcb(a,b)=1,那么稱a,b兩數互質
5.歐拉函數是指設m為正整數,則1,2,3,4.......,m中與m互素的整數的個數記為φ(m),叫做歐拉函
RSA加解密涉及變量
N(n):模數(modulus)
p 和 q :N的兩個因子(factor)
e 和 d:(密鑰) 互為模反數的兩個指數(exponent)
c 和 m:分別是密文和明文,這里一般指的是一個十進制的數還有一個就是n的歐拉函數值
歐拉函數值:r
pow(x, y, z):效果等效pow(x, y)1 % z, 先計算x的y次方,如果存在另一個參數z,需要再對結果進行取模。
RSA 密鑰流程
1.選擇兩個大的參數,計算出模數 N = p * q
2.計算歐拉函數 φ = (p-1) * (q-1),然后選擇一個e(1<e<φ),並且e和φ互質(互質:公約數只有1的兩個整數)
3.選一個整數e,滿足條件1<e<φ(m),且gcd(φ(m),e)=1。
4.取e的模反數d,計算方法為:e * d ≡ 1 (mod φ) (模反元素:如果兩個正整數e和n互質,那么一定可以找到整數d,使得 e * d - 1 被n整除,或者說e * d被n除的余數是1。這時,d就叫做e的“模反元素”。歐拉定理可以用來證明模反元素必然存在。兩個整數a,b,它們除以整數M所得的余數相等:a ≡ b(mod m),比如說5除3余數為2,11除3余數也為2,於是可寫成11 ≡ 5(mod 3)。)
5.對明文m進行加密:c = pow(m, e, N),可以得到密文c。
6.對密文c進行解密:m = pow(c, d, N),可以得到明文m。
7.以{e,n}為公開密鑰,{d,n}為秘密密鑰。
對於RSA加密算法,公鑰{e,n}為公鑰,可以任意公開,破解RSA最直接(亦或是暴力)的方法就是分解整數N,然后計算歐拉函數φ(n)=(p-1) * (q-1),再通過d * e ≡ 1 mod φ(N),即可計算出 d,然后就可以使用私鑰{d,n}通過m = pow(c,d,N)解密明文。
常見攻擊方法
已知p
、q
、e
或者已知n
、e
求出d
import gmpy2
p = gmpy2.mpz(18443)#初始化大整數
q = gmpy2.mpz(49891)
e = gmpy2.mpz(19)
phi_n = (p-1)*(q-1)
d = gmpy2.invert(e,phi_n) # invert(e,r)返回d使得e * d == 1 mod r,如果不存在d,則返回0
print("p={0},q={1},e={2}".format(p,q,e))
print("d is:\n%s"%d)
注:gmpy2:開源的高精度算數運算庫https://blog.csdn.net/x_yhy/article/details/83903367
分解N
得到p
q
可以通過在線網站http://www.factordb.com/index.php
已經求出d
、n
、c
,然后可以求出相應的明文m
#求明文
import gmpy2
n = 920139713 #模數
d = 96849619 #密鑰
c = """
704796792
752211152
274704164
... #密文
"""
result = ""
c_list = c.split()
#print(c_list)
for i in c_list:
result += chr(pow(int(i),d,n))
print(result)
已知c
、e
、n
求m
結合以上兩種方法,在知道n
的前提下可求·p
、q
,利用p
、q
、e
可以求出d
,,從而因為已知d
、n
、c
,求出相應的明文m
利用n的公約數
當題目給出若干個模數n1,n2......,且模數很大。如果兩次加密的n1
和n2
具有相同的素因子,那么我們可以利用歐幾里德算法
直接分解n1
和n2
.從而計算出兩個n
的最大公約數p
:
素因子的定義:對於一個數n來說,將它的因子拆到若干個素數相乘,這些素數被稱為n的素因子。
比如 12可以被拆為2 6
6不是質數,可以繼續拆為2*3
所以最后12的素因子就是 2, 3(不計重復元素)
識別此類題目,通常會發現題目給了若干個n,均不相同,並且都是2048bit,4096bit級別,無法直接分解http://www.factordb.com/index.php,並且明文都沒什么聯系,e也一般取65537。
#-*-coding:utf-8-*-
'''
求兩個數的最大公約數
算法參考:https://zhidao.baidu.com/question/36550887.html
by:reborn
'''
import gmpy2
n1=
n2=
def gys1(n1,n2): #輾轉相除法(歐幾里德算法)
if n1<n2:
n1,n2=n2,n1
while n2!=0:
temp=n1%n2
n1=n2
n2=temp
return n1
def gys2(n1,n2): #更相減損法
while n1!=n2:
if n1<n2:
n1,n2=n2,n1
temp=n1-n2
n1=temp
return n1
p=gys2(n1,n2)
print ("p=",p)
#求q1,q2
q1=n1//p
q2=n2//p
print("q1=",q1)
print("q2=",q2)
#求d_1,d_2
p0 = gmpy2.mpz(p)#初始化大整數
q_1 = gmpy2.mpz(q1)
q_2 = gmpy2.mpz(q2)
e = gmpy2.mpz(65537)
r_1 = (p0-1)*(q_1-1)
r_2 = (p0-1)*(q_2-1)
d_1 = gmpy2.invert(e,r_1) # invert(e,r)返回d使得e * d == 1 mod r,如果不存在d,則返回0
d_2 = gmpy2.invert(e,r_2)
print("d_1=",d_1)
print("d_2=",d_2)
# 求c1,c2
c1=
c2=
m1 = pow(c1, d_1, n1)
m2 = pow(c2, d_2, n2)
print("m1=",m1)
print("m2=",m2)
根據歐幾里德算法
算出的p
之后,再用n
除以p
即可求出q
,由此可以得到的參數有p
、q
、n
、e
,再使用常規方法計算出d
,即可破解密文。
m = pow(c, d, N),可以得到明文m
共模攻擊
如果在RSA的使用中使用了相同的模n對相同的明文m進行了加密,那么就可以在不分解n的情況下還原出明文m的值。
c1 = m^e1 mod n
c2 = m^e2 mod n
識別:非常簡單,若干次加密,每次n都一樣,明文根據題意也一樣即可。
已知私鑰文件、c求m
題目中給出了私鑰文件private.pem和flag.enc
pem文件通常是包含了-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----,是 Base64 編碼的二進制內容
使用私鑰解密密文的方式
使用openssl工具
利用如下命令:
rsautl -decrypt -in flag.enc(密文名稱) -inkey private.pem
已知公鑰文件、c求m
題目中給出了public.pem和密文flag.enc
openssl rsa -pubin -text -modulus -in warmup -in pubkey.pem
[提取出pubkey.pem中的參數]
得到n,化為十進制
將n分解為P,q
python rsatool.py -o private.pem -e 65537 -p 275127860351348928173285174381581152299 -q 319576316814478949870590164193048041239
[使用rsatool生成私鑰文件: private.pem]
openssl rsautl -decrypt -in flag.enc -inkey private.pem
低加密指數攻擊
在RSA中e也稱為加密指數。由於e是可以隨意選取的,選取小一點的e可以縮短加密時間,但是選取不當的話,就會造成安全問題。
e=3時的小明文攻擊
當e=3時,如果明文過小,導致明文的三次方仍然小於n,那么通過直接對密文三次開方,即可得到明文。
(1)m3<n,也就是說m3=c;
(2)m3>n,即(m3+kn)mod n=c(爆破k,不知道k取什么值)。
- 第一種情況 根據
c = pow(m, e, N)
可知:
當e=3時,如果明文過小,導致明文的三次方仍然小於n,那么通過直接對密文三次開方,即可得到明文。
- 第二種情況 如果明文的三次方比n大,但是不夠大,那么設k,有: c=(m^3+kn)mod n
爆破k,如果(c-kn)能開三次根式,那么可以直接得到明文。
識別:
推薦在e=3的時候首先嘗試這種方法。
openssl rsa -pubin -in pubkey.pem (讀取公鑰內容)
openssl rsa -pubin in pubkey.pem -text(以文本格式輸出公鑰內容),從這一步可以知道e的值
從而判斷為低加密指數攻擊
低加密指數廣播攻擊
低加密指數廣播攻擊,即如果選取的加密指數較低,並且使用了相同的加密指數給一個接收者發送了相同的信息(或者給一群接收者發送了相同的信息),那么可以進行廣播攻擊得到明文。
假如我們需要將一份明文進行多份加密,但是每份使用不同的密鑰,密鑰中的模數n不同但指數e相同且很小,我們只要拿到多份密文和對應的n就可以利用中國剩余定理進行解密。
適用
只要滿足以下情況,我們就可以考慮實用低加密指數廣播攻擊:
1.加密指數e非常小
2.一份明文使用不同的模數n,相同的加密指數e進行多次加密
3.可以拿到每一份加密后的密文和對應的模數n、加密指數e
低加密指數廣播攻擊腳本
# coding:utf8
from struct import pack,unpack
import zlib
import gmpy
def my_parse_number(number):
string = "%x" % number
#if len(string) != 64:
# return ""
erg = []
while string != '':
erg = erg + [chr(int(string[:2], 16))]
string = string[2:]
return ''.join(erg)
def extended_gcd(a, b):
x,y = 0, 1
lastx, lasty = 1, 0
while b:
a, (q, b) = b, divmod(a,b)
x, lastx = lastx-q*x, x
y, lasty = lasty-q*y, y
return (lastx, lasty, a)
def chinese_remainder_theorem(items):
N = 1
for a, n in items:
N *= n
result = 0
for a, n in items:
m = N/n
r, s, d = extended_gcd(n, m)
if d != 1:
N=N/n
continue
#raise "Input not pairwise co-prime"
result += a*s*m
return result % N, N
//中國剩余定理 , 輸入多組c和多組n,以及較小的指數e
sessions=[
{"c": , "e": , "n": },
{"c": , "e": , "n": },
{"c": , "e": , "n": }
]
data = []
for session in sessions:
e=session['e']
n=session['n']
msg=session['c']
data = data + [(msg, n)]
print "Please wait, performing CRT"
x, n = chinese_remainder_theorem(data)
e=session['e']
realnum = gmpy.mpz(x).root(e)[0].digits()
print my_parse_number(int(realnum))
n可以分解為多個素數
使用公鑰加密和使用私鑰解密流程(中國剩余定理):
准備
首先,我們需要在在生成私鑰公鑰時,多生成幾個數:
我們的d是e對phi(n)的逆元,我們現在需要另外2個逆元(分別是對(p-1)和(q-1)的),既
1:計算dp,使得dp * e = 1 mod(p-1)
2:計算dq,使得dq * e = 1 mod(q-1)
此外需要第三個元素,既q對p的逆元
3:計算qInv,使得qInv * q = 1 mod p
1 2 3 都作為私鑰的一部分。
dp = d mod p-1
dq = d mod q-1
計算:
使用公鑰加密:
若要加密明文m,則需要計算c = m^e mod n,c為密文。
使用私鑰解密:
1:m1=c^dp mod p
2:m2=c^dq mod q
3:h= (qInv((m1 - m2)mod p)) mod p
4:m = m2 + hq
m就是明文。
例:n=17947
e=3
c=8363
m=???
import gmpy2
n=17947
p=137
q=131
e=3
c=8363
dp=gmpy2.invert(e,p-1)
dq=gmpy2.invert(e,q-1)
m1=pow(c,dp,p)
m2=pow(c,dq,q)
qInv=gmpy2.invert(q,p)
h=(qInv*((m1-m2)% p)) % p
m=m2+h*q
print(m)
多素數
例:n=p1p2p3=2279269
p1=137
p2=131
p3=127
e=19
預先計算:
dp = 19^-1 mod 137-1 = 43
dq = 19^-1 mod 131-1 = 89
dr = 19^-1 mod 127-1 = 73
若要解密密文 768924,則先計算
1:m1=768924^43 mod 137 = 102
2:m2=768924^89 mod 131 = 120
3:m3=768924^73 mod 127 = 5
等式1與等式2連列方程組計算:
qInv = 114
h = (qInv*((m1 - m2)mod p)) mod p = 3
m12 = m2 + h*q = 120 + 3*131 = 513
所以等式1與等式2的通用解為:513+k1*(131*137)
所以結合等式3問題可以變為:
m1=513 p=17947
m2=5 q=127
qInv*q≡ 1 mod p ——>qInv=10316
h = (10316*((513 - 5)mod 17947)) mod 17947 =4
m = 5 + 4*127 = 513
......
jiaoben
import gmpy2
n=2279269
p1=137
p2=131
p3=127
e=19
c=768924
dp1=gmpy2.invert(e,p1-1)
dp2=gmpy2.invert(e,p2-1)
dp3=gmpy2.invert(e,p3-1)
m1=pow(c,dp1,p1)
m2=pow(c,dp2,p2)
m3=pow(c,dp3,p3)
qInv1=gmpy2.invert(p2,p1)
h1=(qInv1*((m1-m2) % p1)) % p1
m4=m2+h1*p2
p4=p1*p2
qInv2=gmpy2.invert(p3,p4)
h2=(qInv2*((m4-m3)% p4)) % p4
m=m3+h2*p3
print(m)
dp、dq泄露
dp = d mod p-1
dq = d mod q-1
這種參數是為了讓解密的時候更快速而產生的
已知p,q,dp,dq,c求m
import gmpy2
import binascii
import libnum
def decrypt(dp,dq,p,q,c):
InvQ = gmpy2.invert(q,p)
mp = pow(c,dp,p)
mq = pow(c,dq,q) #求冪取模運算
m=(((mp-mq)*InvQ)%p)*q+mq #求明文公式
print (binascii.unhexlify(hex(m)[2:]))
print(libnum.n2s(m))
p =
q =
dp =
dq =
c =
decrypt(dp,dq,p,q,c)
已知e,n,dp,c求m
import gmpy2
import libnum
import binascii
def getd(n,e,dp):
for i in range(1,e): #在范圍(1,e)之間進行遍歷
if (dp*e-1)%i == 0:
if n%(((dp*e-1)/i)+1)==0: #存在p,使得n能被p整除
p=((dp*e-1)/i)+1
q=n/(((dp*e-1)/i)+1)
phi = (p-1)*(q-1) #歐拉定理
d = gmpy2.invert(e,phi)%phi #求模逆
return d
e =
n =
dp =
c =
d=getd(n,e,dp)
m=pow(c,d,n) #快速求冪取模運算
print(binascii.unhexlify(hex(m)[2:])) #16進制轉文本
print(libnum.n2s(m))
https://blog.csdn.net/qq_42939527/article/details/105202716
已知n,r求p,q
核心是通過n和r解出p和q
1.二分法,求得p,q
2.RSATool2v17中,輸入p,q,r,e,得到d (腳本也可)
3.通過m=pow(c,d,n)
注意:有時題目有要求,解出的可能是m乘上某一個參數,這是需要仔細審題
4.轉字符,得到flag
腳本
import gmpy2
import numpy as np
np.set_printoptions(suppress=True)
n=gmpy2.mpz(14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199...)
r=gmpy2.mpz(14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199...)
c1=n-r+1
print (c1)
l=c1/2
r=c1
#p=(l+r)/2
#y=p*(c1-p)
while l<r:
p=(l+r)/2
y=p*(c1-p)
if y==n:
print (p)
break
if y>n:
print (y>n)
l=p
else:
print (y<n)
r=p
print ("done")
q=c1-p
print q
/*
if p>q:
p,q=q,p
factor2 = 2021 * p + 2020 * q
if factor2 < 0:
factor2 = (-1) * factor2
_P=sympy.nextprime(factor2)
*/