deffactor(): n_4t = n * 4 * t for k inrange(100000): delta = k ** 2 + n_4t if gmpy2.iroot(delta, 2)[1]: p = (-k + gmpy2.iroot(delta, 2)[0]) / (2 * t) q = t * p + k if p * q == n: return p, q
from Crypto.Util.number import size,bytes_to_long,getStrongPrime from itertools import combinations
msg = bytes_to_long("Your secret flag is : flag{**************************}") e = 65537 pri = []
f = open('cipherx.txt', 'w')
for i inrange(5): pri.append(getStrongPrime(1024,65537)) for k in combinations(pri, 2): n = k[0] * k[1] print k[0], k[1] f.write(str(pow(msg, e, n)) + '\n')
defegcd(a, b): if a == 0: return b, 0, 1 else: g, y, x = egcd(b % a, a) return g, x - b // a * y, y rs = egcd(e1, e2) r = rs[0] s = rs[1] # 求模反元素 if r < 0: r = -r c1 = invert(c1, n) elif s < 0: s = -s c2 = invert(c2, n) m = pow(c1, s1, NN) * pow(c2, s2, N) % N
低加密指数攻击
$e$特别小的情况下.已知:
攻击者可以从小到大枚举$k$.
1 2 3 4 5 6 7 8 9
from Crypto.Util.number import * from gmpy2 import iroot
defsmallEattack(c, e, n): for i inrange(10 ** 10): res = iroot(n * i + c, e) if res[1]: print(res) break
低加密指数广播攻击
假设n1,n2,n3互素,否则可计算最大公约数直接得到p,q`,根据中国剩余定理,在$e = 3$时,可以得到:$c_x\equiv m^3 mod n_1 n_2 n_3$,通过对$c_x$进行三次开方就可以求得明文.
1 2 3 4 5 6 7 8
from CRT import chinese_remainder from gmpy2 import iroot
# datas = ... nList = [x[1] for x in datas] cList = [x[2] for x in datas]
import random defgcd(a, b): if a < b: a, b = b, a while b != 0: temp = a % b a = b b = temp return a
defgetpq(n, e, d): p = 1 q = 1 while p == 1and q == 1: k = d * e - 1 g = random.randint(0, n) while p == 1and q == 1and k % 2 == 0: k /= 2 y = pow(g, k, n) if y != 1and gcd(y - 1, n) > 1: p = gcd(y - 1, n) q = n / p return p, q
n = 0xa66791dc6988168de7ab77419bb7fb0c001c62710270075142942e19a8d8c51d053b3e3782a1de5dc5af4ebe99468170114a1dfe67cdc9a9af55d655620bbab e = 0x10001 d = 0x123c5b61ba36edb1d3679904199a89ea80c09b9122e1400c09adcf7784676d01d23356a7d44d6bd8bd50e94bfc723fa87d8862b75177691c11d757692df8881 p, q = getpq(n, e, d)
低解密指数攻击
Wiener Attack
如果满足:$d<\frac{1}{3}N^\frac{1}{4}$和$q < p < 2p$,就可以通过$Wiener Attack$分解$n$.
defSimplify(ctnf): # 连分数化简 numerator = 0 denominator = 1 for x in ctnf[::-1]: numerator, denominator = denominator, x * denominator + numerator return (numerator, denominator)
defcontinuedFra(x, y): # 展开为连分数 cF = [] while y: cF += [x / y] x, y = y, x % y return cF
defcalculateFrac(x, y): # 生成d,k的估计值 cF = continuedFra(x, y) cF = map(Simplify, (cF[0:i] for i in xrange(1, len(cF)))) return cF
defsolve_pq(a, b, c): # 解韦达定理,已知p+q和n求p,q. # (p-q)^2 = (p+q)^2 - 4n. par = gmpy2.isqrt(b * b - 4 * a * c) return (-b + par) / (2 * a), (-b - par) / (2 * a)
defwienerAttack(e, n): for (d, k) in calculateFrac(e, n): if k == 0: continue if (e * d - 1) % k != 0: continue phi = (e * d - 1) / k p, q = solve_pq(1, n - phi + 1, n) #n - phi + 1 = p + q if p * q == n: returnabs(int(p)), abs(int(q)) print'not find!'
Bob is extremely paranoid, so he decided that just one RSA encryption is not enough. Before sending his message to Alice, he forced her to create 5 public keys so he could encrypt his message 5 times! Show him that he still is not secure. Here are the 5 public keys that Bob used, each in the format of (N, e): (9247606623523847772698953161616455664821867183571218056970099751301682205123115716089486799837447397925308887976775994817175994945760278197527909621793469, 11) (9247606623523847772698953161616455664821867183571218056970099751301682205123115716089486799837447397925308887976775994817175994945760278197527909621793469, 41) (9247606623523847772698953161616455664821867183571218056970099751301682205123115716089486799837447397925308887976775994817175994945760278197527909621793469, 67623079903) (9247606623523847772698953161616455664821867183571218056970099751301682205123115716089486799837447397925308887976775994817175994945760278197527909621793469, 5161910578063) (9247606623523847772698953161616455664821867183571218056970099751301682205123115716089486799837447397925308887976775994817175994945760278197527909621793469, 175238643578591220695210061216092361657427152135258210375005373467710731238260448371371798471959129039441888531548193154205671) Here is his encrypted message: 7117565509436551004326380884878672285722722211683863300406979545670706419248965442464045826652880670654603049188012705474321735863639519103720255725251120
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
import ContinuedFractions, Arithmetic, RSAvulnerableKeyGenerator defhack_RSA(e,n): frac = ContinuedFractions.rational_to_contfrac(e, n) convergents = ContinuedFractions.convergents_from_contfrac(frac) for (k, d) in convergents: if k != 0and (e * d - 1) % k == 0: phi = (e * d - 1) // k s = n - phi + 1 discr = s * s - 4 * n if (discr >= 0): t = Arithmetic.is_perfect_square(discr) if t != -1and (s + t) % 2 == 0: print("Hacked!") return d
c = 7117565509436551004326380884878672285722722211683863300406979545670706419248965442464045826652880670654603049188012705474321735863639519103720255725251120 e = e1 * e2 * e3 * e4 * e5 % n1 d = hack_RSA(e, n1) m = pow(c, d, n1)
Boneh and Durfee attack
当$d$较小时,满足$\frac{1}{3}N^\frac{1}{4} \le d < N^{0.292}$时可以利用该攻击.
e = 0x3 b = 0x9e67d3a220a3dcf6fc4742052621f543b8c78d5d9813e69272e65ac676672446e5c88887e8bfdfc92ec87ec74c16350e6b539e3bd910b000000000000000000 n = 0xa1888c641a05aeb81b3d1686317a86f104791fe1d570a5b11209f45d09ea401d255a70744e7a2d39520e359c23a9f1209ee47f496dbd279e62ee1648b3a277ced8825298274322e0a7a86deea282676310a73b6bb946fc924c34ac6c8784ff559bf9a004c03fb167ef54aaea90ce587f2f3074b40d7f632022ec8fb12e659953 c = 0x93145ece45f416a11e5e9475518f165365456183c361500c2f78aff263028c90f20b7d97205f54e21f3bcc8a556b457889fde3719d0a0f9c9646f3f0d0a1e4bee0f259f023168fe8cc0511848c1635022fcc20b6088084585e2f8055a9d1b1d6bdb228087900bf7c6d42298f8e45c451562c816e2303990834c94e580bf0cbd1 kbits = 72 PR.<x> = PolynomialRing(Zmod(n)) f = (x + b)^e - c x0 = f.small_roots(X = 2^kbits, beta = 1)[0]
Factoring with High Bits Known
当知道一个公钥中模数$N$的一个因子的较高位时,就有一定几率来分解$N$.
1 2 3 4 5 6 7 8 9 10
n = 0x241ac918f708fff645d3d6e24315e5bb045c02e788c2b7f74b2b83484ce9c0285b6c54d99e2a601a386237d666db2805175e7cc86a733a97aeaab63486133103e30c1dca09741819026bd3ea8d08746d1d38df63c025c1793bdc7e38d194a30b492aadf9e31a6c1240a65db49e061b48f1f2ae949ac9e7e0992ed24f9c01578d p_fake = 0x2c1e75652df018588875c7ab60472abf26a234bc1bfc1b685888fb5ded29ab5b93f5105c1e9b46912368e626777a873200000000000000000000000000000000 pbits = 1024 kbits = 130 pbar = p_fake & (2^pbits - 2^kbits) PR.<x> = PolynomialRing(Zmod(n)) f = x + pbar x0 = f.small_roots(X = 2^kbits, beta = 0.4)[0] #X – an absolute bound for the root (default: see above) #beta – compute a root mod b where b is a factor of N and b≥N^β
defpartial_p(p0, kbits, n): PR.<x> = PolynomialRing(Zmod(n)) nbits = n.nbits() f = 2^kbits * x + p0 f = f.monic() roots = f.small_roots(X = 2^(nbits // 2 - kbits), beta = 0.3) if roots: x0 = roots[0] p = gcd(2 ^ kbits * x0 + p0, n) return ZZ(p)
deffind_p(d0, kbits, e, n): X = var('X') for k in xrange(1, e + 1): results = solve_mod([e * d0 * X - k * X * (n - X + 1) + k * n == X], 2^kbits) for x in results: p0 = ZZ(x[0]) p = partial_p(p0, kbits, n) if p: return p if __name__ == '__main__': e = 3 n = 57569201048993475052349187244752169754165154575782760003851777813767048953051839288528137121670999884309849815765999616346303792471518639080697166767644957046582385785721102370288806038187956032505761532789716009522131450217010629338000241936036185205038814391205848232364006349213836317806903032515194407739 nbits = n.nbits() kbits = floor(nbits * 0.5) d0 = 1244848677959253796774387650148978357579294769878147704641867595620534030329181934099194560059806799908134954814673426128260540575360296026444649631806619 p = find_p(d0, kbits, e, n) q = n//p print inverse_mod(e, (p - 1))
for i inrange(1, e + 1): if (dp * e - 1) % i == 0: if n % (((dp * e - 1) / i) + 1) == 0: p = ((dp * e - 1) / i) + 1 q = n / p phi = (p - 1)*(q - 1) d = gmpy2.invert(e, phi) % phi print libnum.n2s(pow(c, d, n))
同理可以获取$f(2P)$和$f(4P)$.如果返回的是$(0,0)$,那么有$P < \frac{N}{4}$,如果返回的是$(0,1)$,那么有$\frac{N}{4} < P <\frac{N}{2}$,如果返回的是$(0,1)$,那么有$\frac{N}{2} < P < \frac{3N}{4}$,如果返回的是$(1,1)$,那么有$\frac{3N}{4} < P < N$.
from pwn import * import libnum import re from binascii import unhexlify
deforacle(c): l = [] for i inrange(20): p.sendline(str(c)) s = p.recvuntil("temp_c") l.append(int(re.findall("l\s*=\s*([0-9]*)", s)[0])) flag0 = 0 flag2 = 0 for i inrange(20): if l[i] % 2 != 0: flag0 = 1 if l[i] > 10000: flag2 = 1 return [flag2, flag0]
defmain(): ss = p.recvuntil("temp_c") N = int(re.findall("N\s*=\s*(\d+)",ss)[0]) e = int(re.findall("e\s*=\s*(\d+)",ss)[0]) c = int(re.findall("c\s*=\s*(\d+)",ss)[0]) size = libnum.len_in_bits(N) c = (pow(2, e, N) * c) % N LB = 0 UB = N i = 1 while LB != UB: flag = oracle(c) if flag[1] % 2==0: UB = (LB + UB) / 2 else: LB = (LB + UB) / 2 c = (pow(2, e, N) * c) % N i += 1 for i inrange(-128, 128, 0): LB += i ifpow(LB, e, N) == C: print unhexlify(hex(LB)[2:-1]) exit(0)
defgetM2(a, b, c1, c2, n): a3 = pow(a, 3, n) b3 = pow(b, 3, n) first = c1 - a3 * c2 + 2 * b3 first = first % n second = 3 * b * (a3 * c2 - b3) second = second % n third = second * gmpy2.invert(first, n) third = third % n fourth = (third + b) * gmpy2.invert(a, n) return fourth % n m = getM2(a, b, c1, c2, n) - padding2
defCipolla_algorithm(p, n): a = randint(1, p) w = a ** 2 - n whilepow(w, (p - 1) / 2, p) != p - 1: a = randint(1, p) w = a ** 2 - n times = (p + 1) / 2 k1 = 1 k2 = 0 first = True sum1 = 1 sum2 = 0 while times != 0: if first: k1, k2 = power(k1, k2, a, 1, w, p) first = False else: k1, k2 = power(k1, k2, k1, k2, w, p) if times & 1: sum1, sum2 = power(sum1, sum2, k1, k2, w, p) times >>= 1 return sum1
defCRT(c, n): for i inrange(len(n)): for j inrange(i + 1, len(n)): assert gcd(n[i], n[j]) == 1 assertlen(c) == len(n) N = reduce(lambda a, b: a * b, n) x = 0 for i, j inzip(c, n): N_i = N / j N_i_1 = invert(N_i, j) x += i * N_i * N_i_1 return x % N
if __name__ == '__main__': p = getPrime(512) while p % 4 != 1: p = getPrime(512) q = getPrime(512) while q % 4 != 1: q = getPrime(512) n = p * q m = s2n('this is plaintext') c = pow(m, 2, n) print'm is '+str(m)
get_x1 = Cipolla_algorithm(p, c) get_x2 = Cipolla_algorithm(q, c)
assertpow(get_x1, 2, p) == c % p assertpow(get_x2, 2, q) == c % q
print'possible m :' + str(CRT([c11, c21], [p, q])) print'possible m :' + str(CRT([c11, c22], [p, q])) print'possible m :' + str(CRT([c12, c21], [p, q])) print'possible m :' + str(CRT([c12, c22], [p, q]))
n次同余方程
题目给的是$m^e \equiv c \pmod{n}$,其中$p$,$q$已知但是$e$和$\phi(n)$不互素的话,就可以先解出$m^2 \equiv c \pmod{p},m^2 \equiv c \pmod{q}$再用$CRT$联合.
针对$m^3 \equiv c \pmod{p}$,$python$有$sympy$库可以进行处理,算法应该是利用的原根进行计算,所以当$p$比较大的时候计算也是很难的,而且也不是一定有原根的.
1 2 3 4 5 6 7 8 9
from Crypto.Util.number import getPrime from random import randint from sympy.ntheory.residue_ntheory import nthroot_mod p = getPrime(150) x = randint(1, p) n = pow(x, 4, p) x1 = nthroot_mod(n, 4, p, all_roots = False) assertpow(x1, 4, p) == n print x1
partially_decoded_ct = [ct] for i inrange(5): new_partially_decoded_ct = [] for ct_p in partially_decoded_ct: new_ct_p = rabin.decrypt(ct_p) new_partially_decoded_ct.extend(list(new_ct_p)) partially_decoded_ct = set(new_partially_decoded_ct)
素数因子较小
计算$x^e=c mod p$可能的根$x$,即为明文$m$对素数因子$p$的余数,依次求出$m$对所有素数因子的余数,再利用中国剩余定理$CRT$即可求得明文$m$.
m = solve_crt([m1, m2, m3], [q1, q2, p]) #重新解密一个rsa n = q1 * q2 f = (q1 - 1) * (q2 - 1) m = m % n d = invmod(7, f) m = pow(m, d, n) print n2s(gmpy2.iroot(m, 2)[0])
p = 0x00f23799c031b942026e420769b74d22fa2114428189139c43c366c6ab8367c6b3d6f821449aafb2058b0e6ed964fa0ad45fb306f96376e80823a72b58101919e50acad3b5e6d079e7ff9218ed6df6edbef536742714ce88b2e717f45af53ef0d04c89faf01c80b28e764973aba27726c85c0236e8756a865c03577722bac5e391 q = 0x00c9d24330fa4945cfe1e5d6912d6bde0231035a1cc8d8ae67d949347b895f8d579bce2adaf37c568957b17a6564dbf80d36d81e4622ab30e02132b0155aefbd3912a27c625a9b7b05bc72217039f5aa88c20cbf9871c3228e9d80d9106f94b11c1f50c40c96862b5cd6b6f781883dd2eff80a059d3ca027af6a03edeb34a7390f n = p*q
f = 2^kbits*x + p0 f = f.monic() roots = f.small_roots(X=2^(nbits//2-kbits), beta=0.3) # find root < 2^(nbits//2-kbits) with factor >= n^0.3 if roots: x0 = roots[0] p = gcd(2^kbits*x0 + p0, n) return ZZ(p)
deffind_p(d0, kbits, e, n): X = var('X')
for k in xrange(1, e+1): results = solve_mod([e*d0*X - k*X*(n-X+1) + k*n == X], 2^kbits) for x in results: p0 = ZZ(x[0]) p = partial_p(p0, kbits, n) if p: return p
if __name__ == '__main__': n = 0x00bef498e6eb2cffe71312da47ab89d2c47db7438ea2cfa992ddddbc2a01978001fc51e286e6ebf028396cdb8b3323c60e6b9d50cd84187cf7f48e3875a2f0890f70b02333ad89db2923863ce146562286f63fb0a1d0198e3a6862ba5ac12e85a5c6d0d27cb1c81bdf69cc5bc95b8001a2f744517f9437b4ddd5a076fc0e9a5de1a7a268c40f31aa29e8dc27c0b3a182299ca7a9335b4bd4585452f6107c238e486c98dd73a5f9862e9e80b152f53381c72f897107551c281259ac3ee32c4b4f46cc03127d1bf699acd0266f3c6729253c70da0c69b1560fa172735709866b375b6eba294e1ce8b46fba798ba380080b4bf9603998cac199d9cd46e30ae8da9e7f e = 3 d = 0x7f4dbb449cc8aa9a0cb73c2fc7b1372da924d7b46c8a710c93e9281c010faaabfd8bec59ef47f5702648925cccc284099d138b33ad65a8a54db425a3c1f5b0b4f5cac22273b13cc617aed340d98ec1af4ed5206be011097c459726e72b7459192f35e1a8768567ea46883d30e7aaabc1fa2d8baa62cfcde93915a4a809bc3e9547bb07e1ecca16e51078312e89f0561e31b55db8b0ea5bc87a6ca7464a3d7c28a68c60e2ba88fe6a7d2b300d723e549910a987da89fc0a1c0de197a3d62c501b1f0e819891b1c32a0d6c233f2a285df87bb9e5c6c72d983ff3e706696bba639f573f9c3646968f02f3a615a438e20bb7c38d53621079f2899547a95350f3abeb
n = 0x00bef498e6eb2cffe71312da47ab89d2c47db7438ea2cfa992ddddbc2a01978001fc51e286e6ebf028396cdb8b3323c60e6b9d50cd84187cf7f48e3875a2f0890f70b02333ad89db2923863ce146562286f63fb0a1d0198e3a6862ba5ac12e85a5c6d0d27cb1c81bdf69cc5bc95b8001a2f744517f9437b4ddd5a076fc0e9a5de1a7a268c40f31aa29e8dc27c0b3a182299ca7a9335b4bd4585452f6107c238e486c98dd73a5f9862e9e80b152f53381c72f897107551c281259ac3ee32c4b4f46cc03127d1bf699acd0266f3c6729253c70da0c69b1560fa172735709866b375b6eba294e1ce8b46fba798ba380080b4bf9603998cac199d9cd46e30ae8da9e7f e = 3
from sage.allimport * n = 0xeb4f8c45336c229371fd73a252b24dd3bf8b3cdc1bb1864f140fd63c88d47c44ba228bebe223fe53c7eaf88678b780821a6660b2726506216554990a5dda178ee04a47c7f1974fc8f8268d081bbb2be7e7353ccf36fecfce5f5f82722d064928f2d60844373c52b4d1db9dc41f7f16807c5b4356c4d2290811e25c51ef1227aa6e893d37dd8743e391fa638d77d0c55e4fb331576602128333d4be95f06523521e7511b39fc20111c88f2635b67e3531684d58ea6574179b5e63a862d073241f5ff91c97a45aa3d8e3287d8161a97728d2e19d72669f39f9e6ad10677bb563bdef30d0dcfa719c2f1836bd02b73d21dbecc11717b54c45d415d3f423ce6dfd8dL p2 =0xfb2151c701f7667b53822fe625b95edee00c3a947b234eca47903ef62fb128d813a9c1acb328f3f7181d24ce31814cd1a69ac4b61b269e2b0eb7fbaabe9633d33a36d0715b4cd386L
e = 0x10001 pbits = 1024
kbits = 448 p2 = p2 << kbits PR.<x> = PolynomialRing(Zmod(n)) f = x + p2 roots = f.small_roots(X=2^kbits, beta=0.4)
if roots: p = p2+int(roots[0]) print"n: ", n print"p: ", p print"q: ", n/p
如果已知位数不足576位,可以爆破所缺位的二进制,达到576位了再用算法恢复完整的p
高校运维
题目给了enc,n,e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
from flag import FLAG from Crypto.Util.number import * import os import gmpy2
p = getPrime(2048) q = gmpy2.next_prime(bytes_to_long(os.urandom(32)*8)) n = p*q e = 65537
m = bytes_to_long(FLAG) enc = pow(m,e,n)
withopen("enc","wb") as f: f.write(str(enc)+"\n") f.write(str(n)+"\n")
n=120807710153113702551615579080626349972702435654213602643278178850601270671946229285821528380336690426317604059622034599839001416930715968066016772516322170847232613450387418879151680919583407733398280475244970196660246303755390654445483988806163997943960045202300170321033632884706732013873256539789027552900587666422370948750842536533923935656875965991272731558533581897633592458155859972323709278905289729445241014357315813048740496355157322217479024997808766708783034169626351658483634985294355975778256304622956911622334081421352132051371869608818591717661111189285407351900021893457439899221542567630909004930602528901255429064258255953091799356807896508016798627878778491866567622281528441807056062152648122769596905617532839645811871242955534491003544450002957748265702306031022676181061669831693628120952508570252446308607118097142440911574131249381253267168309302968966178203572103064042325655007707720847432033652545364390235670894288288369956445797446648862192044259720010703057599068467348014822871417162946598110099990800849922664801383108437169282005803013729663209291895365964487113632471631243676196750054390014101920098216264290734689252677221687512705895162185620154778448467145374612676883160397044672382343419867 t=bytes_to_long(('\x00'*31+'\x01')*8) K = Zmod(n) P.<x> = PolynomialRing(K) for i inrange(1452,1453): f= x * t +i r =f.monic().small_roots(beta=0.45) if r: print i,r p = r * t + i print p