RSA-p和q挨得很近(费马分解)

一、基础

最简单的就是拿yafu直接分解,但是我们得知道那个算法的原理,

        就是现在p,q是两个素数,而且他俩在素数序列里面就是一前一后的关系。所以我们要把他俩的乘积开根号得到的结果一定是在p,q之间的一个数字,(而且一定不是素数,因为p,q就是紧邻的两个素数)。

           那我们找这个开方出来的数字的下一个素数,一定是q,因此我们再让n/q就可以得到两个素数。(这是第一种方法,必须得保证两个数为素数,而且挨得很近才行,我们还会介绍第二种方法,即使有一个不是素数也可以解决。)

'''生成两个挨得近的素数p,q'''
p = getPrime(512)
q = gmpy2.next_prime(p)
n=p*q
print(p)
print(q)
print(n)

'''开始破解'''
temp=gmpy2.iroot(n,2)[0]  #下面会介绍这个函数的用法
p=gmpy2.next_prime(temp)
q=n//p
print(p)
print(q)

 插入:介绍gmpy2的iroot函数,这个函数专门用来进行大数开根号,gmpy2.iroot(n,t)。n就是大整数,t是你要开几次幂。

n = 25090599929998210805842179282010892518788774707524638919079437546041232793900321811241137174436965015616090258924169447468139697688937237025509597680391508555749204353178672698784759792195141661434163809407121898399491900074041853967956481880690069324590880495900272708614479483939242962853655449365679871506290712104139043498133484229702911989500930216839263081488062817080043580519435821704452166687962258543290861642124944113387024276748328363981567361186204753673408517208885593967345448724464638916881282723847845005156574391814533553317383170531212134424920411849924841268526337807027963471928363537964205561663
print(gmpy2.iroot(n,3))

注意结果的形式:前面开根号的结果,后面的true或false表示是否是整开的。比如你要对8开3次幂,后面就是true。

 二、第二种方法。平方差遍历法。

核心总结就是:令a是n的"中间值"(\sqrt{n}),然后让a以步长为1自增遍历,直到pow(a,2)-n的结果可以正好开方为止。那个结果开方就是b。

RSA-p和q挨得很近(费马分解)_第1张图片

 整个例子看看。

'''生成两个挨得近的素数p,q'''
p = getPrime(512)
q = gmpy2.next_prime(p)
n=p*q
print(p)
print(q)
print(n)



print('开始破解')
'''开始破解'''
a=gmpy2.iroot(n,2)[0]
while 1:   #破解出来一组就行了,一般也就一组,挨得很近的话很快就出来了,如果长时间还没出来就可以换方法了,不要指望着他遍历所有的,到死也弄不完。
    B2=pow(a,2)-n
    a+=1
    if gmpy2.is_square(B2):
        b=gmpy2.iroot(B2,2)[0]
        p=a+b
        q=a-b
        print(p)
        print(q)
        break

三、dl在文章里给出了一道绿城杯的原题,我在后面附加了一道moectf的原题。

①、绿城杯

完整的EXP可以看这位dl的 WP,但是他没有将原理,一笔带过了。

(8条消息) 2021年“绿城杯”网络安全大赛-Crypto-RSA2-PLUS_夜白君的博客-CSDN博客

题目:

from Crypto.Util.number import *
import gmpy2
from flag import flag
assert flag[:5]==b'flag{'
​
m1 = bytes_to_long(flag[:20])
p  = getPrime(512)
p1 = gmpy2.next_prime(p)
q  = getPrime(512)
q1 = gmpy2.next_prime(q)


n1 = p*q*p1*q1
print('n1 =',n1)
e = 0x10001
c1 = pow(m1,e,n1)
print('c1 =',c1)
​
#n1 = 6348779979606280884589422188738902470575876294643492831465947360363568026280963989291591157710389629216109615274754718329987990551836115660879103234129921943824061416396264358110216047994331119920503431491509529604742468032906950984256964560405062345280120526771439940278606226153077959057882262745273394986607004406770035459301695806378598890589432538916219821477777021460189140081521779103226953544426441823244765828342973086422949017937701261348963541035128661464068769033772390320426795044617751909787914185985911277628404632533530390761257251552073493697518547350246993679844132297414094727147161169548160586911
#c1 = 6201882078995455673376327652982610102807874783073703018551044780440620679217833227711395689114659144506630609087600915116940111002026241056808189658969089532597757995423694966667948250438579639890580690392400661711864264184444018345499567505424672090632235109624193289954785503512742400960515331371813467034511130432319427185134018830006918682733848618201088649690422818940385123599468595766345668931882249779415788129316594083269412221804774856038796248038700275509397599351533280014908894068141056694660319816046357462684688942519849441237878018480036145051967731081582598773076490918572392784684372694103015244826

 分析:

p  = getPrime(512)
p1 = gmpy2.next_prime(p)
q  = getPrime(512)
q1 = gmpy2.next_prime(q)

可以看出,p和p1紧紧挨着,q和q1紧紧挨着。所以要是把n因式分解的话可能得到7种结果。

RSA-p和q挨得很近(费马分解)_第2张图片

由于p和p1挨得近,q和q1挨得近。所以我们知道,下面这两组解离n的中间值(\sqrt{n})很近。这并不是真正意义上的中间值,只是说相对于别的解,pq和p1*q1(p*q1和q*p1)挨着相对很近。这就了我们利用上面漏洞的机会。

RSA-p和q挨得很近(费马分解)_第3张图片

所以那么我们如果使用上面的第二个脚本那种方法(费马分解),我们就可以在短时间内爆破出这两组非质数的解(但是别的组由于隔得远,所以得花很长时间,不过别的那几组解对我们来说也没什么用),而且因为我们知道它其实是从‘中间’开始往两边爆破的,一旦我们两组解都有了,求它们的公因数就能得到q或p的值,从而得到所有4个因子的值。

exp如下: 

n1 = 6348779979606280884589422188738902470575876294643492831465947360363568026280963989291591157710389629216109615274754718329987990551836115660879103234129921943824061416396264358110216047994331119920503431491509529604742468032906950984256964560405062345280120526771439940278606226153077959057882262745273394986607004406770035459301695806378598890589432538916219821477777021460189140081521779103226953544426441823244765828342973086422949017937701261348963541035128661464068769033772390320426795044617751909787914185985911277628404632533530390761257251552073493697518547350246993679844132297414094727147161169548160586911
c1 = 6201882078995455673376327652982610102807874783073703018551044780440620679217833227711395689114659144506630609087600915116940111002026241056808189658969089532597757995423694966667948250438579639890580690392400661711864264184444018345499567505424672090632235109624193289954785503512742400960515331371813467034511130432319427185134018830006918682733848618201088649690422818940385123599468595766345668931882249779415788129316594083269412221804774856038796248038700275509397599351533280014908894068141056694660319816046357462684688942519849441237878018480036145051967731081582598773076490918572392784684372694103015244826

def factor(n):
    list = []
    a = gmpy2.iroot(n, 2)[0]
    while 1:
        B2 = pow(a, 2) - n
        if gmpy2.is_square(B2):
            b = gmpy2.iroot(B2, 2)[0]
            pq = a - b
            p1q1 = a + b
            list.append([pq, p1q1])
            print(pq)
            print(p1q1)
            if len(list) == 2:
                break
        a += 1  # 注意这个a的位置,别放错了,你要放到前面就错了,不信试试
    return list


list = factor(n1)

'''两组解'''
X1, Y1 = list[0]
X2, Y2 = list[1]
'''求公约数'''
p = gmpy2.gcd(X1, X2)
q = gmpy2.gcd(Y1, Y2)
'''求另一个数字'''
p1 = X2 // p
q1 = Y2 // q

'''RSA解密'''
f = (p - 1) * (q - 1) * (p1 - 1) * (q1 - 1)
print(long_to_bytes(pow(c1, gmpy2.invert(e, f), n1)))
#关于最后一步也算是RSA的一个小变种,往常都是两个大素数,现在一下子变成四个了,不管几个,都按照原来的模子。

②、moectf 

题目:

import gmpy2
from Crypto.Util.number import * 
p = getPrime(2048) 
q = gmpy2.next_prime(p)
for i in range(3600):
    if i%100 ==0:
        print(i)
    q = gmpy2.next_prime(q)

n = p * q
e = 0x10001

flag = xxx
m = bytes_to_long(flag)
c = pow(m,e,n)
print(c)
print(n)

'''
5883797662470459824355663245986072888499217007658131616834157815812099907584034205088255553387720712715657503553785084616903197734118992506040765948815581238738585159640841277023597023582148173041980600751980206228524475872232080917683822098342300418744639304147771013376863895727877847094151770079046205501266017838881847833528612089868825489776289686550273385136080255799772961155599801690997753649087689949021276549323525754963020408864310302166537661098308581259246052869844362142747080042122189010627048397501817473817946566885487595098504403459522534124404289032779842658407728856164570059823567667669076044563549721918886430160041337156249733571322684187916005175717585587552966989348534775572282369273898182367851689305440672199427492706130124832744127722533758962606513875787129378871099575729793745175327897215145024490319291830298017471555440811147903390803597635585696411407922981136489077349754222355529320548946411677051716584081079246752768224289803323109047467790868885987703125118276891234633889937243303027095375365791207055516900563280115276282761652663098154769929217653527103304045922204641545963828632051715956492613217136463227530538723452005224696385225174844198627387638874395654771260577791169209134146482
371836308886540426192412096148744468186415625392487977879857531835902736615143801798286888910032757343063307437491756141584074211336204232321625860256198232674594289958977877151673559656231508894335267778421247120253811435320830719345924114507429603444867321985950626826991173077205178053362583897682032724665933945097478196733856621304091584618890629791164070168813615231192565754075364366134730406435348259862415601279551372742556900223695625597120400693500365067997937729674171334150610370961480163812842105971064886537235753552837000236613769498285320938741476925731411679897178247509473618923405834484514661807520252213326586104301410231354079662448182315435504639054167776200376152713328322609890314052157497227912497420886067642369853988427179097601651852373889536473835216949882465614231082448644133599830105850384251912580667211045553849789111685933572279734855596537588506333965238289830492091608095939699649204953662409772326162756616403885752077614154740093699490363803868230526757718971893753734479487055548790771458190489276504470984092766005111535651632518882006617378156289619913024674060627173821938856436490090896481522906788847664717355154445108253949612361422754030509689952049972855384980134472281224218581516679
'''

分析:

可以看出: p和q很近,但不是完全挨着的,所以第一种方法就失效了,我们只能用第二种方法了。令a是n的中间值n的中间值(\sqrt{n})然后去遍历,看什么时候可以得到一个正正好好的平方数。 

c = 5883797662470459824355663245986072888499217007658131616834157815812099907584034205088255553387720712715657503553785084616903197734118992506040765948815581238738585159640841277023597023582148173041980600751980206228524475872232080917683822098342300418744639304147771013376863895727877847094151770079046205501266017838881847833528612089868825489776289686550273385136080255799772961155599801690997753649087689949021276549323525754963020408864310302166537661098308581259246052869844362142747080042122189010627048397501817473817946566885487595098504403459522534124404289032779842658407728856164570059823567667669076044563549721918886430160041337156249733571322684187916005175717585587552966989348534775572282369273898182367851689305440672199427492706130124832744127722533758962606513875787129378871099575729793745175327897215145024490319291830298017471555440811147903390803597635585696411407922981136489077349754222355529320548946411677051716584081079246752768224289803323109047467790868885987703125118276891234633889937243303027095375365791207055516900563280115276282761652663098154769929217653527103304045922204641545963828632051715956492613217136463227530538723452005224696385225174844198627387638874395654771260577791169209134146482
n = 371836308886540426192412096148744468186415625392487977879857531835902736615143801798286888910032757343063307437491756141584074211336204232321625860256198232674594289958977877151673559656231508894335267778421247120253811435320830719345924114507429603444867321985950626826991173077205178053362583897682032724665933945097478196733856621304091584618890629791164070168813615231192565754075364366134730406435348259862415601279551372742556900223695625597120400693500365067997937729674171334150610370961480163812842105971064886537235753552837000236613769498285320938741476925731411679897178247509473618923405834484514661807520252213326586104301410231354079662448182315435504639054167776200376152713328322609890314052157497227912497420886067642369853988427179097601651852373889536473835216949882465614231082448644133599830105850384251912580667211045553849789111685933572279734855596537588506333965238289830492091608095939699649204953662409772326162756616403885752077614154740093699490363803868230526757718971893753734479487055548790771458190489276504470984092766005111535651632518882006617378156289619913024674060627173821938856436490090896481522906788847664717355154445108253949612361422754030509689952049972855384980134472281224218581516679
e = 0x10001


def factor(n):
    a = gmpy2.iroot(n, 2)[0]
    while 1:
        B2 = pow(a, 2) - n
        if gmpy2.is_square(B2):
            b = gmpy2.iroot(B2, 2)[0]
            p = a + b
            q = a - b
            return p, q
        a += 1  # 千万别忘了a的自增步长为1

p,q=factor(n)
f = (p - 1) * (q - 1)
d = gmpy2.invert(e, f)
print(long_to_bytes(pow(c, d, n)))

参考这个dl的文章:

浅析RSA因子大小相近时分解因子攻击方法 - FreeBuf网络安全行业门户

你可能感兴趣的:(ctf)