RSA两种特殊攻击情况 P和q的不当分解 |p-q|很大 时,一定存在某个参数ip较小,这里我们假设p较小我们可以通过穷举法分解模数,但是很少遇到
|p-q|较小
$(p+q)^2/4-n=(p-q)^2/4$;
|p-q|较小,那么(p-q)/2和n^1/2^也比较接近
顺序检查 √nn 的每一个整数 x,直到找到一个 x 使得 x2−nx2−n 是平方数,记为 y2y2
那么 x2−n=y2x2−n=y2,进而根据平方差公式即可分解 N
举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import gmpy2from Crypto.Util.number import getPrimeimport random p = getPrime(1024 ) q = gmpy2.next_prime(p, p + 10000 ) n = p * qprint ("p=" ,p)print ("q=" ,q)print ("n=" ,n)def factor (n ): a = gmpy2.iroot(n, 2 )[0 ] while True : a+=1 b2 = a * a - n if gmpy2.is_square(b2): b2 = gmpy2.mpz(b2) b, xflag = gmpy2.iroot(b2, 2 ) assert xflag return (a - b, a + b)print (factor(n))
一个变式题目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from Crypto.Util.number import *import gmpy2from flag import flagassert 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*q1print ('n1 =' ,n1) e = 0x10001 c1 = pow (m1,e,n1)print ('c1 =' ,c1)
这里有n=四个素数因子乘积
上述可以分为两种。。(p,q)(p1,q1)…(p,q1)(p1,q)这两组每组两个元素乘积相近,可以利用费马分解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import gmpy2from Crypto.Util.number import getPrime, inverse, long_to_bytesimport randomfrom gmpy2 import gcdfrom pywin.Demos.cmdserver import flagsfrom scipy.signal import qspline1dfrom sympy import factor_listdef factor (n ): a = gmpy2.iroot(n, 2 )[0 ] factor_list = [] while True : a+=1 b2 = a * a - n if gmpy2.is_square(b2): b2 = gmpy2.mpz(b2) b, xflag = gmpy2.iroot(b2, 2 ) assert xflag factor_list.append([a + b, a - b]) if len (factor_list) == 2 : break return factor_list e = 0x10001 c1 = 6201882078995455673376327652982610102807874783073703018551044780440620679217833227711395689114659144506630609087600915116940111002026241056808189658969089532597757995423694966667948250438579639890580690392400661711864264184444018345499567505424672090632235109624193289954785503512742400960515331371813467034511130432319427185134018830006918682733848618201088649690422818940385123599468595766345668931882249779415788129316594083269412221804774856038796248038700275509397599351533280014908894068141056694660319816046357462684688942519849441237878018480036145051967731081582598773076490918572392784684372694103015244826 n = 6348779979606280884589422188738902470575876294643492831465947360363568026280963989291591157710389629216109615274754718329987990551836115660879103234129921943824061416396264358110216047994331119920503431491509529604742468032906950984256964560405062345280120526771439940278606226153077959057882262745273394986607004406770035459301695806378598890589432538916219821477777021460189140081521779103226953544426441823244765828342973086422949017937701261348963541035128661464068769033772390320426795044617751909787914185985911277628404632533530390761257251552073493697518547350246993679844132297414094727147161169548160586911 factor_list=factor(n) X1,Y1=factor_list[0 ] X2,Y2=factor_list[1 ] p=gcd(X1,X2) p1=gcd(Y1,Y2) q=X1//p q1=Y1//p1 Phi=(p-1 )*(q-1 )*(p1-1 )* (q1-1 )print ("p=" ,p)print ("q=" ,q)print ("q1=" ,q1)print ("p1" ,p1)print ("Phi=" ,Phi) d = inverse(e, Phi) flag=pow (c1,d,n)print ("flag=" ,long_to_bytes(flag),"}" )''' flag= b'flag{Euler_funct1ons' }'''
维纳攻击 使用条件 私钥d<1/3N^1/4^,选择较大的公钥e,用*e* ′代替*e*,其中*e* ′ = *e* + *k* ⋅ *λ* ( *N* ),*k*为某个较大的数。当*e* ′ 足够大时,即*e* ′ > N 3/2 ,则无论d 有多小,都无法实施维纳攻击。假设p与q二进制长度相等即p<q<2p,可以得到p+q<3p<3(n)^0.5^
欧拉函数φ(N)=N-p-q+1 满足N-3(N)^0.5^<φ(N)<N
维纳攻击
维纳的方法
ed=1+k*φ(N)
|$e\over φ(N) $-$k\over d$|=$1 \over φ(n)$
φ(N)约等于N,K/gd约等于e/—>|$e\over N$-$k\over d$|=|$k(p+q-1)-1\over Nd$|
要使上述式子<1/(2d^2^)
p+q<3(N)^0.5^,0<K<d可以推断,|$e\over N$-$k\over d$|<3/N)^0.5^
所以d<3/(n)^0.25^
ed g =k (p −1)(q −1)+g ,k >g
同时除以k,k/g<1可以忽略
⌊ke d**g ⌋=(p −1)(q −1)
(pq −(p −1)(q −1)+1)/2=(p+q)/2
($(p+q)\over 2$)^2^-pq=($p-q\over 2$)^2^ #可以用这个判断连分数收敛是否为所需的k/(dg)
例题sagemath上的连分数扩展函数实现更便捷
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 def possible (e,alist,N ): for x in alist: if x==0 : continue phi=(e*x.denominator()-1 )//x.numerator() if (N-phi+1 )%2 ==0 and sqrt(pow ((N-phi+1 )//2 ,2 )-N).is_integer(): (p,q)=var('p q' ) x=solve([(p-1 )*(q-1 )==phi,p*q==N],p,q) return x else : continue def wienner_attack (e,N ): c=continued_fraction(e/N) alist=c.convergents() return possible(e,alist,N)''' x.denominator(): 这个函数返回分数 x 的分母。 x.numerator(): 这个函数返回分数 x 的分子。 e * x.denominator(): 这个部分将 e 乘以 x 的分母。 e * x.denominator() - 1: '''
这个代码返回的是p和q
rrrrrrsa–一道题
这是一个关于rsa-wiener攻击拓展的推导
1 2 3 4 5 6 7 8 9 10 P1 = getPrime(1038 ) P2 = sympy.nextprime(P1)assert (P2 - P1 < 1000 ) Q1 = getPrime(512 ) Q2 = sympy.nextprime(Q1) N1 = P1 * P1 * Q1 N2 = P2 * P2 * Q2
刚好可以应用在这里。
很明显是满足条件的。所以Q1/Q2是N1/N2的收敛连分数扩展
N1/N2=(P1/P1)^2^*(Q1/Q2)
Q1/Q1>N1/N2,N1<N2,Q1<Q2
n1/n2<Q1/Q2<1,然后我们用
因为Q1和Q2都是素数,我们可以直接得到Q1和Q2
并用N1%Q1来判断是否符合条件
解题代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 from Crypto.Util.number import long_to_bytes, inverseimport gmpy2def possible (alist,N1 ): for x in alist: if x==0 : continue Q1=x.numerator() if N1%Q1==0 and Q1!=1 : Q2=x.denominator() return Q1,Q2 else : continue def wienner_attack (N1,N2 ): c=continued_fraction(N1/N2) alist=c.convergents() return possible(alist,N1) N1= N2= c1= c2= E1= E2= t=wienner_attack(N1,N2) Q1=t[0 ] Q2=t[1 ]print (Q1)print (Q2) P2=gmpy2.iroot(N2//Q2,2 )[0 ] P1=gmpy2.iroot(N1//Q1,2 )[0 ] phi1=(P1-1 )*P1*(Q1-1 ) phi2=(P2-1 )*P2*(Q2-1 ) d1=gmpy2.invert(E1,phi1) d2=gmpy2.invert(E2,phi2) m1=gmpy2.powmod(c1,d1,N1) m2=gmpy2.powmod(c2,d2,N2) f1=long_to_bytes(m1) f2=long_to_bytes(m2)print (f1,f2)''' 11628371843051760370952910026406764366191062991235308941262037248377376991693250742343307155422036713746576338866595433599862614339347536916226536644210947 11628371843051760370952910026406764366191062991235308941262037248377376991693250742343307155422036713746576338866595433599862614339347536916226536644211929 b'GWHT{3aadab41754799' b'f978669d53e64a3aca} '''
扩展维纳 参考:论文:https://dunkirkturbo.github.io/2020/05/04/WriteUp-De1CTF2020-Crypto/howgrave-graham1999.pdf
ctf-wiki-扩展维纳
维纳的方法 这里我们忽略前面提到的q<p<2q的条件。利用其他进行维纳攻击
这里有个前提,e约等于n,S约等于(n)^0.5^(大概就是2的指数差不多)
λ(N)=lcm(p-1,q-1)….g=gcd(p-1,q-1). s=1-p-q
$ed-k*λ(N)=1$
$edg-kN=g+ks$
$e\over N$-$k\over dg$=($k\over dg$)($s\over N$)+$1\over dN$
这里原论文的证明近似的量有点多,不够严谨,不能算是严格的维纳证明,最开始的那个更细节
GUO的方法 考虑存在两个e。
$e_1d_1g-k_1(p-1)(p-1)=g$
$e_2d_2g-k_2(p-1)(q-1)=g$
化简可以得到
$k_2d_1e_1−k_1d_2e_2=k_2−k_1$
同时除以$k_2d_1e_2$
$e_1\over e_2$-$k_1d_2\over k_2d_1$=$k_2-k_1\over k_2d_1e_2$
设$d_i<N^α$
$k_2-k_1\over k_2d_1e_2$≈N^−(1+α)^
为了满足上述定理
2(k 2−k 1)d 1k 2<e 2
但是通过$k_1d_2\over k_2 d_1$找到分解N不是很现实,这个扩展结合了上述两种方法利用格来求
扩展维纳攻击 两个关系:
$d_ige_i−k_iN=g+k_is$ —M
k_id_je_j−k_jd_ie_i=k_i−k_j —Gi,j
我们假设$d_i$ 和$k_i $都小于N^αn^,且g很小,s≈N^1/2^。可以注意到$W_i$ 和Gi的右侧非常小,实际上分别最多为N^1/2+α^ 和N^α^。
接下来我们可以利用四个等式构造格
$k_1 k_2=k_1 k_2$
$d_1ge_1−k_1N=g+k_1s$
$k_1d_2e_2−k_2d_1e_1=k_1−k_2$
$d_1d_2g^2e_1e_2−d_1gk_2e_1N−d_2gk_1e_2N+k_1k_2N_2=(g+k_1s)(g+k_2s)$
可以构造如下格
视作A*L=B
根据假设
明显|L|≈2N,但是明显N^2α^,N^1/2+2α^,N^α2^,N^1+2α^.明显这里不符合定理要求需要构造一下,
$M_1=N$^1/2^,$M_2=N$^1+α^,得到
根据上述条件
α ≤5/14
把B2当作求出的最短向量。A=B*L^-1^。这是我们就能得到A,A[1]/A[0]=$d_1g\over k_1$
φ(N)=$edg\over k$-$g\over k$=$edg\over k$
羊城杯 simple.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 from Crypto.Util.number import *from Crypto.Cipher import DESimport gmpy2from secret import flagimport random key = "abcdefgh" def des_encrypt (m ): des = DES.new(key, DES.MODE_ECB) res = des.encrypt(m) return resdef gen_key (): p = getPrime(2048 ) q = getPrime(2048 ) n = p * q bit = n.bit_length() phi_n = (p - 1 ) * (q - 1 ) num = random.randint(1 , 100 ) while True : u = getPrime(bit / 4 - num) if gmpy2.gcd(u, phi_n) != 1 : continue t = gmpy2.invert(u, phi_n) e = bytes_to_long(des_encrypt(long_to_bytes(t))) if gmpy2.gcd(e, phi_n) == 1 : break return (n, e) P = getPrime(1024 ) Q = getPrime(1024 ) N = P * Q E = 65537 lcm = gmpy2.lcm(P-1 , Q-1 ) e1 = gmpy2.invert(getPrime(730 ), lcm) e2 = gmpy2.invert(getPrime(730 ), lcm) m = bytes_to_long(flag) c = pow (m, E, N)print "N = " + str (N)print "e2 = " + str (e2)print "c = " + str (c) _n, _e = gen_key() _c = pow (e1, _e, _n)print "_n = " + str (_n)print "_e = " + str (_e)print "_c = " + str (_c)
我们需要先求出e1,再通过e1和e2找到lcm,再通过lcm找到d和m
1 2 3 4 5 6 7 8 def des_decrypt (m): des = DES.new (key, DES.MODE_ECB) res = des.decrypt (m) return res ciphertext = long_to_bytes (_e) decrypted_data = des_decrypt (ciphertext) _t=bytes_to_long (decrypted_data)print (_t)利用这个先求出_t
t = gmpy2.invert(u, phi_n),这里t满足维纳攻击的条件可以利用t求出phi_n再解出_d和e1。_
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from gmpy2 import invert def possible(e,alist,N ): for x in alist: if x==0: continue phi =(e*x.denominator()-1 )//x.numerator() if (N -phi +1 )%2 ==0 and sqrt (pow((N -phi +1 )//2 ,2 )-N ).is_integer(): (p,q)=var ('p q') Phi =phi return Phi else: continue def wienner_attack(e,N ): c=continued_fraction(e/N )#获取连分数集合 alist=c.convergents()#利用连分数得到连分数收敛 return possible(e,alist,N ) _t = _c = _n = _e = phi =wienner_attack(_t ,_n )d1 =invert(_e,phi )e1 =pow(_c,d1 ,_n ) print(e1 )
利用e1构造矩阵得到phi在求出m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from gmpy2 import invert from Crypto.Util .number import bytes_to_long, long_to_bytes c = e2 = e1 = N = a = 5 ./14 M1=N**0.5 M2= N **(a+1 ) D = diagonal_matrix (ZZ,[N,M1,M2,1] ) M=matrix (ZZ,[[1,-N,0,N**2] ,[0,e1,-e1,-e1*N] ,[0,0,e2,-e2*N] ,[0,0,0,e1*e2] ])*D L=M.LLL () t=vector (ZZ,L[0] )x =t*M**(-1 ) phi = int (x [1 ]/x[0 ]*e1) d = invert (0 x10001,phi) m=int (pow (c,d,N))print (long_to_bytes(m) )