系列文章目录
前言
RSA 加密算法是一种非对称加密算法。在公开密钥加密和电子商业中 RSA 被广泛使用。RSA 算法的可靠性由极大整数因数分解的难度决定。换言之,对一极大整数做因数分解愈困难,RSA 算法愈可靠。假如有人找到一种快速因数分解的算法的话,那么用 RSA 加密的信息的可靠性就肯定会极度下降。但找到这样的算法的可能性是非常小的。如今,只有短的 RSA 密钥才可能被强力方式解破。到 2017 年为止,还没有任何可靠的攻击 RSA 算法的方式。
一、基本原理
公钥与私钥的产生
- 随机选择两个不同大质数 p 和 q,计算 N=p×q
- 根据欧拉函数,求得 φ(N)=φ(p)φ(q)=(p−1)(q−1)
- 选择一个小于 φ(N)的整数 e,使 e 和 φ(N)互质。并求得 e 关于 φ(N)的模反元素,命名为 d,有 ed≡1(modφ(N))
- 将 p 和 q 的记录销毁
此时,(N,e)是公钥,(N,d)是私钥。
消息加密
消息加密
二、正确性证明
要验证RSA的正确性即证明
因为ed≡1modφ(n),所以ed=kφ(n)+1 (k为整数)
所以
因此等价证明
三、十二种基本的攻击方式
1.小明文攻击(⭐⭐)
p = getPrime(5120)
q = getPrime(5120)
n = 1392208858696945158251408085300402884210409327605255122395601049457847957306648819174395014931778575812308192875319127224095733396726388842605854427013313599830150182564652493067830031524869535522568868597852507293377043240832819715539722122306829543983051745406887140154364256267942350230636870212935356815475345989038318923389132101208917912083817520799490534351545373438629367819085151041702754019127891155542476560972125790519584018715794669416759039955180436830478697704458250990786586357019211642837879273967620287257818400267757312623543898635740200839249361087580046496969637043238450499583879116276661827372750638403042213043389905183760455450595752578482968202040860053524276678108325784161897719093223308370971388068813420487879756084379519128232693549989942550047529174783976176943482484756074638704076740157397067892580297182979526978967352014250386678155843772313996171396582929433057131989693861316408604436872931427368192437680361830753162284661119995125858203931094922686321756465463988790748131178263745308042820480140189644732824717255521633534750635979508673908361269979175726073254050574688259969290376926807033728165164896588540691889207252105903436659968119091774687841336252628343233161941187968365128093917171537997137001140227677728923114895809278926486615010954871408034272872411042537353956193868948909981683850857262457369506288525323882308421700421661036191853577105238753230479541719001794464585534292774768292358961920606891227776349593589481547577148801600196035588544512224775960892265021565124673788852875005526313525709353599584812394138968970647681759439523307392275602626903789154682706839530654070108096741181206975334567778238856983205004289416400671597321919876279909765650782227834097342294844294386380646928266942749144240020420237153276705785759403019072953506982997681174635673907151856644499332322321579088035719680421458310273802481031746012298208449699089203065699598926690947025679591160106357130634946357609420125223580319677387654711233585375013067828291928349946599077331636017784447090096340360087970540477975170379810969501197027775838769222713506443846417124839450184827707739588007707714623211453528342755023849716924694572679150284882978804641986457119009272574734697043321033091757474387114449914271460113979531460465175717705674905568446670579332667139075523255580471183372714211547822093365025438653384719374474230360983878837077517864405475258349436531094649276628214288499716485354283135575921258757214288792410583835467572916298688718758374714560819702413058421373661892101033513816116981698045524150518509405086125781764762145577981637953775680403132163846782252745029783387112660812179706752454175492501665442704630131729362621965258498471247871904163412798544329515689112368523703890083138721480476796720323855371775568097188216621368341228806795058046403892301673157631331636430392885315997250027372621883549649614866115616619234953579196607399899485002042456482969222428121605212017146571466818179341621066715472184636758016242256725063854155219754299817717414423725704356940589670902509021070871847017199593680033
e = 97
phi = (p-1)*(q-1)
c = 79418540691422578656139651796213224829563266521211325595707569487401417030874358531413674275017334363641194166574500833916574827523075402219754470871728896772312056257743844227927800121160288525434484105786180547178403828613331285574461293211150728313415280329153597549251599876668080073528625299164784405291297754331374420687599875173508778209038236713812747215157059659564867241144529476211694011692007565732793105429228730285249025627762831080251661835294067942958070742202862164086250986988784472568266652325462247009294865764533077164470382743735937483173523682749295196383707694876943634298051820105410771024861599560176707940488888601355473498847493259474613261665538825299531665650233837474894442826242097663456648233384047622152817959729025415665280499618951478005159820575701483220155180955748454167435711033379910310483689839303198822665341421591234243891877857520663120183063591818656508336831518527692847950877186870610083654117153248336928856886934232488927132245720058243202637258025864487122393335118118013444397135476780564523166548571927547341556616683070253790368891423731046742936358877118104790084195711730135202600692806999992373490439385845158780392894927697171401722699273071306493916233696254958735540772870249139252741670476667099529502282392011715616110876451102234600267482991583515122052976378641894214562203326290939184469206074418127906704847146567350085797480500249400491003993809769407575997740985283755035509654310797061339563655229926356893455738361409861102662109994984398860070584568014471082484198504331014073023689622378943694856212172718725529473812336321642429261822836311084518735006587545920946664595488768239633950124822001162595168106106356115962424210028401369438479550293237915944302351566624339603616714683958384871326105542659559877758488581425288668613061792514360263277530824203967659102107889148367539858141289229124274098921748855341045565232484417195620758495861456624842263649414501657786484816662971421962216348340311859717286253287173293151613310383128702607971580042308515018120559903265609733911340589091613087560931098833849573462572181625894166772788435459280323623477689159384354671220634694792005231505741029567734616435905915192606539962414882105254409847885996949466940350184140166614950171110955365828033747003120697209120916652982201967537088553504504252785632280900095976870510754563400828951684036526240669112248351928072177486091157562600003336544767896806392523395037345770580482363058065676920013089896399387769312374871419827762872050800055872960573607645266626972865053489632548224840580503746879607167797904430560935476705014800973841917939689270919224595772574781478285359220463175042728750523639669204218676238297875644055563803457896409252533724486937378974745777400567080239687055154021761534918106133195512030935957251049812753269173090858930245212145938555697547499155977225759702066548720079477737104010668116693232798415289735481194922014811945312893853446826780868861295203942063380964100360870361328125
由加密函数可推出当明文很小时
不会模n丢失信息,所以c直接开e次方就能得到明文m
推荐使用gmpy2库的iroot()函数来处理大数开根,函数用法如下iroot(10, 3)
代表对10开3次方,返回结果为(mpz(2), False)
,mpz
为gmpy2
包中对整数的封装类,看作一个整数即可,第二项代表这个数是否为完全开放的结果
2.低加密指数攻击(⭐⭐)
p = getPrime(512)
q = getPrime(512)
n = p*q
e = 3
phi = (p-1)*(q-1)
c = pow(m, e, n)
注意到此时密钥对中的公钥e十分的小,可以考虑低加密指数攻击。
可以表示成c=
+k*n,
所以
我们可以在一个比较小的范围(10w,100w,1000w对于计算机来说都算比较小)进行爆破k的值,遍历k再开e次方根的计算量是可以考虑的,最后用iroot函数可以得出答案。
3.Rabin算法攻击(⭐⭐⭐)
p = 67711062621608175960173275013534737889372437946924512522469843485353704013203
q = 91200252033239924238625443698357031288749612243099728355449192607988117291739
assert p%4 == 3 and q%4 == 3
n = p*q
e = 2
c = 5251890478898826530186837207902117236305266861227697352434308106457554098811792713226801824100629792962861125855696719512180887415808454466978721678349614
)
这里注意到p%4==3和q%4==3这两个条件考虑是Rabin算法的特点之一;
首先我们要了解一些前置知识:
1.二次剩余的定义:
设整数a与正整数m互素,
如果有解,则a为模x的二次剩余;
如果无解,则a为模x的二次非剩余;
2.勒让德符号的定义:
设p是奇素数,a是整数,且p∤a,则勒让德符号定义为:
3.欧拉判别法:
设p是奇素数,a是整数,且p∤a,则
4.Rabin
Rabin本身是一种加解密方法,与RSA类似但这个函数不是单射,一个密文能解出4个明文
取两个大素数(p,q)(p,q)满足
加密:
解密:因为p,q∣n 相当于求解:
c是模p的二次剩余,所以得知,带入上式得
所以,
同理,
此时我们得到四组明文,在这四个解中有且仅有一个m使得为正确解。
5.题解
from gmpy2 import *
from Crypto.Util.number import *
c1 = pow(c, (p + 1) // 4, p)
c2 = pow(c, (q + 1) // 4, q)
cp1 = - c1
cp2 = - c2
t1 = invert(p, q) # p的模q逆元
t2 = invert(q, p) # q的模p逆元
n = p*q
#中国剩余定理
m1 = (q * c1 * t2 + p * c2 * t1) % n
m2 = (q * c1 * t2 + p * cp2 * t1) % n
m3 = (q * cp1 * t2 + p * c2 * t1) % n
m4 = (q * cp1 * t2 + p * cp2 * t1) % n
print(long_to_bytes(m1))
print(long_to_bytes(m2))
print(long_to_bytes(m3))
print(long_to_bytes(m4))
4.Wiener攻击(低解密指数攻击)(⭐⭐⭐⭐⭐)
#十三届全国大学生网络安全竞赛 bd
from secret import flag
from Crypto.Util.number import *
m = bytes_to_long(flag)
p = getPrime(512)
q = getPrime(512) #取个512比特的随机质数
N = p * q
phi = (p-1) * (q-1)
while True:
d = getRandomNBitInteger(200) #生成恰好为200比特的随机数
if GCD(d, phi) == 1:
e = inverse(d, phi)
break
c = pow(m, e, N)
print(c, e, N, sep='\n')
# 37625098109081701774571613785279343908814425141123915351527903477451570893536663171806089364574293449414561630485312247061686191366669404389142347972565020570877175992098033759403318443705791866939363061966538210758611679849037990315161035649389943256526167843576617469134413191950908582922902210791377220066
# 46867417013414476511855705167486515292101865210840925173161828985833867821644239088991107524584028941183216735115986313719966458608881689802377181633111389920813814350964315420422257050287517851213109465823444767895817372377616723406116946259672358254060231210263961445286931270444042869857616609048537240249
# 86966590627372918010571457840724456774194080910694231109811773050866217415975647358784246153710824794652840306389428729923771431340699346354646708396564203957270393882105042714920060055401541794748437242707186192941546185666953574082803056612193004258064074902605834799171191314001030749992715155125694272289
这题给了c,e,n,由定义:m=mod n
所以我们只需求出d,即可得到明文m
,且我们知道,
所以,两边同时除以dφ(n)得
引入近视得概念(p-1)(q-1)=n-p-q+1,也就是φ(n)≈n,所以是一个非常小的数忽略不计,因此
那么如果我们能够找到一种办法使得e/n转化为k/d则便能够得到私钥。Wiener便证明了当时,可以通过对e/n进行连分数展开精准覆盖k/d,如果我们发现d很小或者e很大时可以考虑维纳攻击。观察题目,我们可以发现这个e非常的大,猜测使用维纳攻击求出d。
连分数概念
1.使用辗转相除法得到
的连分数
利用辗转相除法(扩展欧几里得算法)计算的渐进分数
,对每一个i,
,
结合 N = p*q 判断能否分解 N,如果不能,计算下一个渐进分数直至分解 N
import gmpy2
def transform(x, y): # 使用辗转相除法将分数 x/y 转为连分数的形式
res = []
while y:
res.append(x // y)
x, y = y, x % y
return res
2、求有限简单连分数的每个渐进分数
def continued_fraction(sub_res):
numerator, denominator = 1, 0
for i in sub_res[::-1]: # 从sublist的后面往前循环
denominator, numerator = numerator, i * numerator + denominator
return numerator,denominator # 得到渐进分数的分母和分子,并返回
# 求解每个渐进分数
def sub_fraction(x,y):
res = transform(x, y)
res = list(map(continued_fraction, (res[0:i] for i in range(1, len(res)+1)))) # 将连分数的结果逐一截取以求渐进分数
return res
3、利用韦达定理求解p,q
如果要求p,q,已知N=pq,φ(N)=(p-1)(q-1)=pq-(p+q)+1
韦达定理可构造一元二次方程
解出两根就是p和q了
# 由 p+q 和 pq 的值通过韦达定理来求解 p 和 q
def get_pq(a, b, c):
par = gmpy2.isqrt(b * b - 4 * a * c) # 由上述可得,开根号一定是整数,因为有解
x1, x2 = (-b + par) // (2 * a), (-b - par) // (2 * a)
return x1, x2
def wienerAttack(e, n):
for (k,d) in sub_fraction(e, n): # 用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数
if k == 0: # 可能会出现连分数的第一个为0的情况,排除
continue
if (e * d - 1) % k != 0: # ed=1 (\pmod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)
continue
phi = (e * d - 1) // k # 这个结果就是 φ(n)
x1, x2 = get_pq(1, n - phi + 1, n) #x^2 + (p+q)x +pq=0
if x1 * x2 == n:
p, q = abs(int(x1)), abs(int(x2)) # 可能会得到两个负数,负负得正未尝不会出现
d = gmpy2.invert(e, (p - 1) * (q - 1)) # 求ed=1 (\pmod φ(n))的结果,也就是e关于 φ(n)的乘法逆元d
return d
print("该方法不适用")
4.题解
import gmpy2
import libnum
from Crypto.Util.number import long_to_bytes
def transform(x,y): #使用辗转相处将分数 x/y 转为连分数的形式
res=[]
while y:
res.append(x//y)
x,y=y,x%y
return res
def continued_fraction(sub_res):
numerator,denominator=1,0
for i in sub_res[::-1]: #从sublist的后面往前循环
denominator,numerator=numerator,i*numerator+denominator
return denominator,numerator #得到渐进分数的分母和分子,并返回
#求解每个渐进分数
def sub_fraction(x,y):
res=transform(x,y)
res=list(map(continued_fraction,(res[0:i] for i in range(1,len(res))))) #将连分数的结果逐一截取以求渐进分数
return res
def get_pq(a,b,c): #由p+q和pq的值通过维达定理来求解p和q
par=gmpy2.isqrt(b*b-4*a*c) #由上述可得,开根号一定是整数,因为有解
x1,x2=(-b+par)//(2*a),(-b-par)//(2*a)
return x1,x2
def wienerAttack(e,n):
for (d,k) in sub_fraction(e,n): #用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数
if k==0: #可能会出现连分数的第一个为0的情况,排除
continue
if (e*d-1)%k!=0: #ed=1 (mod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)
continue
phi=(e*d-1)//k #这个结果就是 φ(n)
px,qy=get_pq(1,n-phi+1,n)
if px*qy==n:
p,q=abs(int(px)),abs(int(qy)) #可能会得到两个负数,负负得正未尝不会出现
d=gmpy2.invert(e,(p-1)*(q-1)) #求ed=1 (mod φ(n))的结果,也就是e关于 φ(n)的乘法逆元d
return d
print("该方法不适用")
e = 46867417013414476511855705167486515292101865210840925173161828985833867821644239088991107524584028941183216735115986313719966458608881689802377181633111389920813814350964315420422257050287517851213109465823444767895817372377616723406116946259672358254060231210263961445286931270444042869857616609048537240249
n = 86966590627372918010571457840724456774194080910694231109811773050866217415975647358784246153710824794652840306389428729923771431340699346354646708396564203957270393882105042714920060055401541794748437242707186192941546185666953574082803056612193004258064074902605834799171191314001030749992715155125694272289
d=wienerAttack(e,n)
print("d=",d)
c= 37625098109081701774571613785279343908814425141123915351527903477451570893536663171806089364574293449414561630485312247061686191366669404389142347972565020570877175992098033759403318443705791866939363061966538210758611679849037990315161035649389943256526167843576617469134413191950908582922902210791377220066
m=pow(c,d,n)
print(long_to_bytes(m))
5.低加密指数广播攻击(⭐⭐⭐)
识别:n非常大,e一般很小。一般拿到的是多组n和c,且只有一个相同的e,e还很小。
可知这题要用到CRT(中国剩余定理)
import gmpy2
import os
from functools import reduce
from Crypto.Util.number import long_to_bytes
# e, n, c
e = 0x3
n=[0x52d483c27cd806550fbe0e37a61af2e7cf5e0efb723dfc81174c918a27627779b21fa3c851e9e94188eaee3d5cd6f752406a43fbecb53e80836ff1e185d3ccd7782ea846c2e91a7b0808986666e0bdadbfb7bdd65670a589a4d2478e9adcafe97c6ee23614bcb2ecc23580f4d2e3cc1ecfec25c50da4bc754dde6c8bfd8d1fc16956c74d8e9196046a01dc9f3024e11461c294f29d7421140732fedacac97b8fe50999117d27943c953f18c4ff4f8c258d839764078d4b6ef6e8591e0ff5563b31a39e6374d0d41c8c46921c25e5904a817ef8e39e5c9b71225a83269693e0b7e3218fc5e5a1e8412ba16e588b3d6ac536dce39fcdfce81eec79979ea6872793L]
c=[0x10652cdfaa6b63f6d7bd1109da08181e500e5643f5b240a9024bfa84d5f2cac9310562978347bb232d63e7289283871efab83d84ff5a7b64a94a79d34cfbd4ef121723ba1f663e514f83f6f01492b4e13e1bb4296d96ea5a353d3bf2edd2f449c03c4a3e995237985a596908adc741f32365]
def CRT(items):
N = reduce(lambda x, y: x * y, (i[1] for i in items))
result = 0
for a, n in items:
m = N // n
d, r, s = gmpy2.gcdext(n, m)
if d != 1:
raise Exception("Input not pairwise co-prime")
result += a * s * m
return result % N, N
data = list(zip(c, n))
x, n = CRT(data)
m = gmpy2.iroot(gmpy2.mpz(x), e)[0].digits()
print('m is: ' + long_to_bytes(m))
提一嘴特殊情况
虽然我们提到的是低加密指数,但这是相对的,当e较大时我们不考虑使用中国剩余定理,而是尝试在众多n中寻找一对可以求其最大公约数以分解出p和q再根据e计算出d来解密。
6.p-1光滑攻击(⭐⭐⭐⭐)
from Crypto.Util.number import *
from random import choice
import gmpy2
'''
def getMyPrime(nbits):
while True:
p = 1
while p.bit_length() <= nbits:
p *= choice(sieve_base)
if isPrime(p+1):
return p+1
p = getMyPrime(256)
q = getMyPrime(256)
'''
n = 53763529836257082401813045869248978487210852880716446938539970599235060144454914000042178896730979463959004404421520555831136502171902051936080825853063287829
e = 65537
c = 50368170865606429432907125510556310647510431461588875539696416879298699197677994843344925466156992948241894107250131926237473102312181031875514294014181272618
对于sieve_base我们可以在vscode中查看其内容描述
# The first 10000 primes used for checking primality.
# This should be enough to eliminate most of the odd
# numbers before needing to do a Rabin-Miller test at all.
说明这道题中p是由多个连续的素数相乘最终+1得到即
1.光滑数的概念
光滑数(smooth number),或译脆数,是一个可以因数分解为小质数乘积的正整数
如果一个整数的所有素因子都不大于B,我们称这个整数是B-Smooth数
2.攻击分析
假设p-1为k-光滑数,存在这样的一个数M
且使得
我们可以看作M=t(p-1)
由费马定理可推出
所以 联系 N=pq 我们可以通过求公因数分解出p,即求
3.Pollard's p-1
算法的攻击结果
也就是说如果我们能够得到M,那么便可以通过求解和n的公因数得到n的素因子。但是我们如何得到M呢,让我们一起来学习Pollard's p-1
算法。
与数论中的许多算法一样,这个算法不一定能跑出结果。
from Crypto.Util.number import *
from random import choice
import gmpy2
def poll(n):
a=2
b=2
while 1:
a = pow(a, b, n)
p = GCD(a-1, n)
if p != 1 and p != n:
break
b += 1
return p
n = 53763529836257082401813045869248978487210852880716446938539970599235060144454914000042178896730979463959004404421520555831136502171902051936080825853063287829
e = 65537
c = 50368170865606429432907125510556310647510431461588875539696416879298699197677994843344925466156992948241894107250131926237473102312181031875514294014181272618
p=poll(n)
q=n//p
phi=(p-1)*(q-1)
d=inverse(e,phi)
print(long_to_bytes(pow(c,d,n)).decode())
其实还可以对pollard‘s p-1的算法进行优化
7.p+1光滑攻击(⭐⭐⭐⭐⭐)
p+1光滑攻击不同于p-1光滑攻击,它比较违反人类思考的惯性,同时需要一定的知识储备才能理解。以下篇幅较长,请耐心阅读。
from Crypto.Util.number import *
from random import choice
def getMyPrime(nbits):
while True:
p = 1
while p.bit_length() <= nbits:
p *= choice(sieve_base)
if isPrime(p-1):
return p-1
p = getMyPrime(256)
q = getMyPrime(256)
n = 63398538193562720708999492397588489035970399414238113344990243900620729661046648078623873637152448697806039260616826648343172207246183989202073562200879290937
e = 65537
c = 26971181342240802276810747395669930355754928952080329914687241779532014305320191048439959934699795162709365987652696472998140484810728817991804469778237933925
1.卢卡斯序列
卢卡斯序列,尤其是卢卡斯数列,是一个在数学上具有重要地位的整数序列,它与斐波那契数列有着密切的关系。
2.Willian‘s p+1光滑攻击算法
以前看不大明白p+1光滑是如何操作的,在明白卢卡斯序列后,William's p+1算法的流程与pollard-p-1算法思想上是一致的,使用扩展卢卡斯序列得到一个p的倍数,再和n求解最大公因数进行因数分解。
代码实现:
def mlucas(v, a, n):
""" Helper function for williams_pp1(). Multiplies along a Lucas sequence modulo n. """
v1, v2 = v, (v**2 - 2) % n
for bit in bin(a)[3:]: v1, v2 = ((v1**2 - 2) % n, (v1*v2 - v) % n) if bit == "0" else ((v1*v2 - v) % n, (v2**2 - 2) % n)
return v1
for v in count(1):
for p in primegen():
e = ilog(isqrt(n), p)
if e == 0: break
for _ in xrange(e): v = mlucas(v, p, n)
g = gcd(v-2, n)
if 1 < g < n: return g # g|n
if g == n: break
3.题解
from Crypto.Util.number import *
from gmpy2 import *
from itertools import count
n = 63398538193562720708999492397588489035970399414238113344990243900620729661046648078623873637152448697806039260616826648343172207246183989202073562200879290937
e = 65537
c = 26971181342240802276810747395669930355754928952080329914687241779532014305320191048439959934699795162709365987652696472998140484810728817991804469778237933925
def mlucas(v, a, n):
v1, v2 = v, (v ** 2 - 2) % n
for bit in bin(a)[3:]: v1, v2 = ((v1 ** 2 - 2) % n, (v1 * v2 - v) % n) if bit == "0" else (
(v1 * v2 - v) % n, (v2 ** 2 - 2) % n)
return v1
def primegen():
yield 2
yield 3
yield 5
yield 7
yield 11
yield 13
ps = primegen() # yay recursion
p = ps.__next__() and ps.__next__()
q, sieve, n = p ** 2, {}, 13
while True:
if n not in sieve:
if n < q:
yield n
else:
next, step = q + 2 * p, 2 * p
while next in sieve:
next += step
sieve[next] = step
p = ps.__next__()
q = p ** 2
else:
step = sieve.pop(n)
next = n + step
while next in sieve:
next += step
sieve[next] = step
n += 2
def ilog(x, b): # greatest integer l such that b**l <= x.
l = 0
while x >= b:
x /= b
l += 1
return l
def attack(n):
for v in count(1):
for p in primegen():
e = ilog(gmpy2.isqrt(n), p)
if e == 0:
break
for _ in range(e):
v = mlucas(v, p, n)
g = GCD(v - 2, n)
if 1 < g < n:
return int(g), int(n // g) # g|n
if g == n:
break
p, q = attack(n)
phi = (p-1)*(q-1)
d = inverse(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m).decode())
8.共模攻击(⭐⭐⭐)
from Crypto.Util.number import *
p = getPrime(512)
q = getPrime(512)
n = 120294155186626082670474649118722298040433501930335450479777638508444129059776534554344361441717048531505985491664356283524886091709370969857047470362547600390987665105196367975719516115980157839088766927450099353377496192206005171597109864609567336679138620134544004766539483664270351472198486955623315909571
e1 = 38317
e2 = 63409
c1 = 42703138696187395030337205860503270214353151588149506110731264952595193757235229215067638858431493587093612397165407221394174690263691095324298012134779703041752810028935711214038835584823385108771901216441784673199846041109074467177891680923593206326788523158180637665813642688824593788192044139055552031622
c2 = 50460092786111470408945316270086812807230253234809303694007902628924057713984397041141665125615735752600114964852157684904429928771531639899496987905067366415806771003121954852465731110629459725994454904159277228514337278105207721011579794604761255522391446534458815389983562890631994726687526070228315925638
找到一组整数使得
要找到这组我们可以使用扩展欧几里得算法
def ex_gcd(a, b):
if b == 0:
return 1, 0, a
else:
x, y, q = ex_gcd(b, a % b)
x, y = y, (x - (a // b) * y)
return x, y, q
完整题解如下:
from Crypto.Util.number import *
def ex_gcd(a, b):
if b == 0:
return 1, 0, a
else:
x, y, q = ex_gcd(b, a % b)
x, y = y, (x - (a // b) * y)
return x, y, q
n = 120294155186626082670474649118722298040433501930335450479777638508444129059776534554344361441717048531505985491664356283524886091709370969857047470362547600390987665105196367975719516115980157839088766927450099353377496192206005171597109864609567336679138620134544004766539483664270351472198486955623315909571
e1 = 38317
e2 = 63409
c1 = 42703138696187395030337205860503270214353151588149506110731264952595193757235229215067638858431493587093612397165407221394174690263691095324298012134779703041752810028935711214038835584823385108771901216441784673199846041109074467177891680923593206326788523158180637665813642688824593788192044139055552031622
c2 = 50460092786111470408945316270086812807230253234809303694007902628924057713984397041141665125615735752600114964852157684904429928771531639899496987905067366415806771003121954852465731110629459725994454904159277228514337278105207721011579794604761255522391446534458815389983562890631994726687526070228315925638
x,y,q=ex_gcd(e1,e2)
m=pow(c1,x,n)*pow(c2,y,n)%n
print(long_to_bytes(m))
9.dp&dq泄露(⭐⭐⭐)
此类题型一般不会给出e,但是给出p和q
from Crypto.Util.number import *
from gmpy2 import *
p = 13070310882303377463944295715444821218324151935347454554272870042925400761984585838979931730897626589859098834802923539617244712852188293321626061072925723
q = 10411551818233737389114520103233235272671271111546186997024935593000298916988792710521511848414549553426943998093077337023514210631662189798921671306236009
c = 62492280219693914005334023569480350249964827909276875032578276064973191654731196407886841145547165693859745313398152742796887457192397932684370631253099255490064673499746314452067588181106154875239985334051909867580794242253066085627399488604907196244465911471895118443199543361883148941963668551684228132814
dp = 11568639544706374912496682299967972464196129347160700749666263275305083977187758414725188926013198988871173614336707804756059951725809300386252339177953017
dq = 3455040841431633020487528316853620383411361966784138992524801280785753201070735373348570840039176552952269927122259706586236960440300255065994052962742469
1.攻击原理
由中国剩余定理可以将分解
2.我们再了解一下欧拉降幂
根据欧拉降幂我们有
通过欧拉降幂我们能够分析出dp和dq的作用是减少原解密算法的幂运算量来提升速度(实际测试能提速4倍)
因此我们由dp,dq,p,q就可以计算出m
3.题解
from Crypto.Util.number import *
from gmpy2 import *
p = 13070310882303377463944295715444821218324151935347454554272870042925400761984585838979931730897626589859098834802923539617244712852188293321626061072925723
q = 10411551818233737389114520103233235272671271111546186997024935593000298916988792710521511848414549553426943998093077337023514210631662189798921671306236009
c = 62492280219693914005334023569480350249964827909276875032578276064973191654731196407886841145547165693859745313398152742796887457192397932684370631253099255490064673499746314452067588181106154875239985334051909867580794242253066085627399488604907196244465911471895118443199543361883148941963668551684228132814
dp = 11568639544706374912496682299967972464196129347160700749666263275305083977187758414725188926013198988871173614336707804756059951725809300386252339177953017
dq = 3455040841431633020487528316853620383411361966784138992524801280785753201070735373348570840039176552952269927122259706586236960440300255065994052962742469
invp = inverse(p, q)
m1 = gmpy2.powmod(c, dp, p)
m2 = gmpy2.powmod(c, dq, q)
m = (((m2 - m1)*invp) % q)*p + m1
print(long_to_bytes(m))
10.dp泄露(⭐⭐⭐)
p = getPrime(512)
q = getPrime(512)
n = p*q
e = 65537
d = inverse(e, (p-1)*(q-1))
dp = d % (p-1)
c = pow(m, e, n)
#n = 79201858340517902370077926747686673001645933420450220163567700296597652438275339093680329918615445030212417351430952656177171126427547284822789947152085534939195866096891005587613262293569611913019639653984932469691636338705418303482885987114085769045348074530172292982433373154900841135911548332400167290083
#c = 70109332985937768446301118795636999352761371683181615470371772202170324747707233792154935611826981798791499937601162039878070094663516868746240133223110650205575807753345252087103328657073552992431511929172241702073381723302143955977662087561904058172777520360991685289300855900793806183473523998422682944404
#dp = 3098334089252415941833934532457314870210700261428241562420857845879512952043729097866485406309479489101668423603305497982177150304625615059119312238777275
1.分析
与上一题不同,这一题中我们有e,但是失去了dq。
由可以推出
,那么我们有
要求出p只需要遍历满足条件的k即可,我们还需要确定k的大致范围
因为dp为模p-1中的有限域,所以dp<p-1,由上述等式左右量变相等可知e>k,所以
所以在k的取值中我们可以看出当题中e比较小时,遍历可行。
2.题解
from Crypto.Util.number import *
n = 79201858340517902370077926747686673001645933420450220163567700296597652438275339093680329918615445030212417351430952656177171126427547284822789947152085534939195866096891005587613262293569611913019639653984932469691636338705418303482885987114085769045348074530172292982433373154900841135911548332400167290083
e = 65537
dp = 3098334089252415941833934532457314870210700261428241562420857845879512952043729097866485406309479489101668423603305497982177150304625615059119312238777275
c = 70109332985937768446301118795636999352761371683181615470371772202170324747707233792154935611826981798791499937601162039878070094663516868746240133223110650205575807753345252087103328657073552992431511929172241702073381723302143955977662087561904058172777520360991685289300855900793806183473523998422682944404
for k in range(1,e+1):
if (dp*e-1)%k==0:
p=(dp*e-1)//k+1
if n%p==0:
q=n//p
phi=(p-1)*(q-1)
d=inverse(e,phi)
m=pow(c,d,n)
break
print(long_to_bytes(m))
11.e比较大的dp泄露(⭐⭐⭐)
from Crypto.Util.number import *
p = getPrime(512)
q = getPrime(512)
n = 108280026722298796068968170303156759745471686664814404724171434502249429011870583595808692893118419248225924869164875379709992190884930717654004006466664403479467573176438601715156464950045121937338569942817256182277141174728470067308962244296992229214749863655518517510026063088263849891990324547823192559069
e = 305691242207901867366357529364270390903
d = inverse(e, (p-1)*(q-1))
dp = 2656631506624565349527023729530989647164022271235521672257622068579788839123502046687139927161669209201953909023994372208117081512139181611949631467292513
c = 26537258289122728220745496185201994733321402056894636636642710319261241111675937946139938310952968353253866895253865273981912174303818938005932883052177988834834575591342856235464380238486868448329727891268391728758132913642966389278296932186703733187105516710825918064228397602264185334108934765627411913661
我们看到给出的条件和上一题差不多,但是e的取值已经达到了128位,这样遍历k显然是跑不动的。
1.分析
我们选取一个与p互素的整数a,使得gcd(a,p)=1,上一题中我们得到
因此,
由费马定理可以继续化简上式,
所以
最后从kp和n中提取公因数就能得到
2.题解
from Crypto.Util.number import *
import gmpy2
a=2
n = 108280026722298796068968170303156759745471686664814404724171434502249429011870583595808692893118419248225924869164875379709992190884930717654004006466664403479467573176438601715156464950045121937338569942817256182277141174728470067308962244296992229214749863655518517510026063088263849891990324547823192559069
e = 305691242207901867366357529364270390903
dp = 2656631506624565349527023729530989647164022271235521672257622068579788839123502046687139927161669209201953909023994372208117081512139181611949631467292513
c = 26537258289122728220745496185201994733321402056894636636642710319261241111675937946139938310952968353253866895253865273981912174303818938005932883052177988834834575591342856235464380238486868448329727891268391728758132913642966389278296932186703733187105516710825918064228397602264185334108934765627411913661
p=gmpy2.gcd(pow(a,dp*e,n)-a,n)
q=n//p
phi=(p-1)*(q-1)
d=inverse(e,phi)
print(long_to_bytes(pow(c,d,n)))
12.由n,e,d求p,q(⭐⭐⭐⭐⭐)
from Crypto.Util.number import *
from gmpy2 import *
p = getPrime(512)
q = getPrime(512)
assert p < q
n = p*q
e = 65537
phi = (p-1)*(q-1)
d = gmpy2.invert(e, phi)
这道题我们不再求解m,而是由n,e,d求解p,q。
1.分析
首先我们计算K=de-1(K是φ(n)的倍数),所以可以写成k=,其中r为奇数,我们先计算
x=,
,
......,
(modN)
随机选取,对于每一个g都有
此处的可以是
,所以
是单位元1(modn)的平方根
同理,
,
也是如此。
根据中国剩余定理,1在模n下会有平方根,其中有两个解是±1,还有两个是±x,±x满足或者
,当x>1且gcd(x-1,n)>1时我们可以将n分解。
结论:我们最终要计算的是
2.题解
from Crypto.Util.number import *
from gmpy2 import *
from hashlib import *
from random import *
n = 113917408220469425995764932761465306974540330325378601642830241920567032775895088098706711486764203845425248022960733155994427766750033219106642310531864450654102562104771892268897793145789045570107312401570269581223945259704851104645493075550316424129401227653740942495625720165869565257394427181127734628103
e = 65537
d = 15762135247924329080208071933121250646888501386858311483546464344350547831176536290630826247188272280853810047335214127264865205744683174860903496832368687060941437002920094364116706593296591581117381565805322046922482804679245558495134876677733584718947309975077159564300049936769192724856722338627154192353
def factor_n_with_ed(n,e,d):
p = 1
q = 1
while p == 1 and q == 1:
k = e*d - 1
g =randint(0,n)
while p==1 and q==1 and k % 2 == 0:
k //= 2
y = pow(g,k,n)
if y>1 and gmpy2.gcd(y-1,n)>1:
p = gmpy2.gcd(y-1,n)
q = n//p
return p,q
p,q=factor_n_with_ed(n,e,d)
print(md5(str(p).encode()).hexdigest())#f56299b1e5339dfebe8ea3d32dd44043
随机选取g后,x的取值序列中,不为1的概率大约为,而序列中的所有元素可以在时间O(
)内有效地计算,其中
。
总结
没有总结。。。嘻嘻