常用工具

分解大素数

factordbhttp://www.factordb.com / API: http://factordb.com/api?query=

yafu (p q 相差过大或过小yafu可分解成功)

sagedivisors(n))(小素数)

Pollard’s p−1python -m primefac -vs -m=p-1 xxxxxxx)(光滑数)

Williams’s p+1python -m primefac -vs -m=p+1 xxxxxxx)(光滑数)

cado-nfs

在线sage环境:

https://sagecell.sagemath.org/

Openssl

 解析加密密钥:

openssl rsa -pubin -text -modulus -in pub.key

生成解密密钥:

python rsatool.py -f PEM -o key.key -p 1 -q 1 -e 1

openssl rsautl -decrypt -inkey key.pem -in flag.enc -out flag

openssl rsautl -decrypt -oaep -inkey key.pem -in flag.enc -out flag (OAEP方式)

脚本生成解密密钥:

# coding=utf-8
import math
import sys
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP rsa_components = (n1, e, int(d1), p, q1)
myrsa = RSA.construct(rsa_components) private = open('private.pem', 'w')
private.write(myrsa.exportKey())
private.close() rsakey = RSA.importKey(myrsa.exportKey())
rsakey = PKCS1_OAEP.new(rsakey)
decrypted = rsakey.decrypt(c_bytes)

脚本集

https://github.com/Ganapati/RsaCtfTool

#用法一:已知公钥(自动求私钥)
$ python3 RsaCtfTool.py --publickey 公钥文件 --uncipherfile 加密文件 #用法二:已知公钥求私钥
$ python3 RsaCtfTool.py --publickey 公钥文件 --private #用法三:密钥格式转换
#把PEM格式的公钥转换为n,e
$ python3 RsaCtfTool.py --dumpkey --key 公钥文件
#把n,e转换为PEM格式
$ python3 RsaCtfTool.py --createpub -n 782837482376192871287312987398172312837182 -e 65537

https://github.com/yifeng-lee/RSA-In-CTF

https://github.com/ValarDragon/CTF-Crypto

常见类型

给p,q,e,c

import gmpy2 as gp
import binascii
p =
q =
e =
c =
n = p*q
phi = (p-1)*(q-1)
d = gp.invert(e,phi)
m = pow(c,d,n)
print(m)
print(bytes.fromhex(hex(m)[2:]))

给n,e,dp,c

import gmpy2 as gp

e =
n =
dp =
c = for x in range(1, e):
if(e*dp%x==1):
p=(e*dp-1)//x+1
if(n%p!=0):
continue
q=n//p
phin=(p-1)*(q-1)
d=gp.invert(e, phin)
m=gp.powmod(c, d, n)
if(len(hex(m)[2:])%2==1):
continue
print('--------------')
print(m)
print(hex(m)[2:])
print(bytes.fromhex(hex(m)[2:]))

变种1:

Hensel lifting for Takagi’s scheme(p.189):

from Crypto.Util.number import *
import gmpy2
p =
dp =
c =
b =
e =
mp1 = pow(c, dp, p)
mp = pow(c, dp - 1, p)
for i in range(1, b - 2):
x = pow(c - pow(mp1, e), 1, p**(i + 1))
y = pow(x * mp * (gmpy2.invert(e, p)), 1, p**(i + 1))
mp1 = mp1 + y
print(long_to_bytes(mp1))

变种2

#Sage
dp0 =
e =
n = F.<x> = PolynomialRing(Zmod(n))
d = inverse_mod(e, n)
for k in range(1, e):
f = (secret << 200) + x + (k - 1) * d
x0 = f.small_roots(X=2 ** (200 + 1), beta=0.44, epsilon=1/32)
if len(x0) != 0:
dp = x0[0] + (secret << 200)
for i in range(2, e):
p = (e * Integer(dp) - 1 + i) // i
if n % p == 0:
break
if p < 0:
continue
else:
print('k = ',k)
print('p = ',p)
print('dp = ',dp)
break

变种3:

beta =
delta =
n = round((1-2*beta-2*delta)/((1-beta)^2-2*delta-beta),6)
m = (1-beta)*n
print(m,n)

# 脚本1
# Sage
def getC(Scale):
C = [[0 for __ in range(Scale)] for _ in range(Scale)]
for i in range(Scale):
for j in range(Scale):
if i == j or j == 0:
C[i][j] = 1
else:
C[i][j] = C[i-1][j-1] + C[i-1][j]
return C def getMatrix(Scale, Mvalue, N, E, Del, Bet):
M = [[0 for __ in range(Scale)] for _ in range(Scale)]
C = getC(Scale)
X, Y = int(pow(N,Del)*(Scale+1)//2), int(pow(N,(Del+Bet))*(Scale+1)//2)
for i in range(Scale):
for j in range(Scale):
M[i][j] = N**max(Mvalue-i,0)*E**(max(i-j,0))*X**(Scale-1-j)*Y**j*C[i][j]*(-1)**j
return M N =
E =
delta = 0.01
beta = 0.37
Scale = 35
Mvalue = 22
M = getMatrix(Scale,Mvalue,N,E,delta,beta)
M = matrix(ZZ,M)
A = M.LLL()[0]
p = []
X = int(pow(N,delta)*(Scale+1)//2)
Y = int(pow(N,(delta+beta))*(Scale+1)//2)
for i in range(Scale):
p.append(A[i]//(X**(Scale-1-i)*Y**i))
PR.<x,y> = PolynomialRing(ZZ)
f = 0
for i in range(Scale):
f += p[i]*x^(Scale-1-i)*y^i
print(f.factor())
# 脚本2
# Sage
N =
e = n = 12
beta = 0.36
delta = 0.02 X = int(N ** delta*(n+1)/2)
Y = int(N ** (delta + beta)*(n+1)/2) def C(a,b):
ret=1
for i in range(b):
ret *= (a-i)
ret /= (b-i)
return ret
def get_Matrix(n,m):
MM=[[0 for __ in range(n)] for _ in range(n)]
for j in range(n):
pN = max(0,m-j)
for i in range(j+1):
MM[j][i] = pow(N,pN)*pow(X,n-i-1)*pow(Y,i)*pow(e,j-i)*C(j,i)*pow(-1,i)
MM = Matrix(ZZ,MM)
return MM M = get_Matrix(n,n//2+1)
L = M.LLL()[0] x,y = var('x'),var('y')
f = 0
for i in range(n):
f += x**(n-i-1) * y**i * (L[i] // pow(X,n-i-1) // pow(Y,i)) print(f.factor())

参考:

Cryptanalysis of Unbalanced RSA with Small CRT-Exponent

https://hash-hash.github.io/2022/05/14/Unbalanced-RSA-with-Small-CRT-Exponent/#An-Approach-Modulo-e

NSSCTF Round#3 - Secure_in_N

from copy import deepcopy
# https://www.iacr.org/archive/pkc2006/39580001/39580001.pdf
# Author: ZM__________J, To1in
N =
e =
alpha = log(e, N)
beta =
delta =
P.<x,y,z>=PolynomialRing(ZZ) X = ceil(2 * N^(alpha + beta + delta - 1))
Y = ceil(2 * N^beta)
Z = ceil(2 * N^(1 - beta)) def f(x,y):
return x*(N-y)+N
def trans(f):
my_tuples = f.exponents(as_ETuples=False)
g = 0
for my_tuple in my_tuples:
exponent = list(my_tuple)
mon = x ^ exponent[0] * y ^ exponent[1] * z ^ exponent[2]
tmp = f.monomial_coefficient(mon) my_minus = min(exponent[1], exponent[2])
exponent[1] -= my_minus
exponent[2] -= my_minus
tmp *= N^my_minus
tmp *= x ^ exponent[0] * y ^ exponent[1] * z ^ exponent[2] g += tmp
return g m = 5 # need to be adjusted according to different situations
tau = ((1 - beta)^2 - delta) / (2 * beta * (1 - beta))
sigma = (1 - beta - delta) / (2 * (1 - beta)) print(sigma * m)
print(tau * m) s = ceil(sigma * m)
t = ceil(tau * m)
my_polynomials = []
for i in range(m+1):
for j in range(m-i+1):
g_ij = trans(e^(m-i) * x^j * z^s * f(x, y)^i)
my_polynomials.append(g_ij) for i in range(m+1):
for j in range(1, t+1):
h_ij = trans(e^(m-i) * y^j * z^s * f(x, y)^i)
my_polynomials.append(h_ij) known_set = set()
new_polynomials = []
my_monomials = [] # construct partial order
while len(my_polynomials) > 0:
for i in range(len(my_polynomials)):
f = my_polynomials[i]
current_monomial_set = set(x^tx * y^ty * z^tz for tx, ty, tz in f.exponents(as_ETuples=False))
delta_set = current_monomial_set - known_set
if len(delta_set) == 1:
new_monomial = list(delta_set)[0]
my_monomials.append(new_monomial)
known_set |= current_monomial_set
new_polynomials.append(f)
my_polynomials.pop(i)
break
else:
raise Exception('GG') my_polynomials = deepcopy(new_polynomials) nrows = len(my_polynomials)
ncols = len(my_monomials)
L = [[0 for j in range(ncols)] for i in range(nrows)] for i in range(nrows):
g_scale = my_polynomials[i](X * x, Y * y, Z * z)
for j in range(ncols):
L[i][j] = g_scale.monomial_coefficient(my_monomials[j]) # remove N^j
for i in range(nrows):
Lii = L[i][i]
N_Power = 1
while (Lii % N == 0):
N_Power *= N
Lii //= N
L[i][i] = Lii
for j in range(ncols):
if (j != i):
L[i][j] = (L[i][j] * inverse_mod(N_Power, e^m)) L = Matrix(ZZ, L)
nrows = L.nrows() L = L.LLL()
# Recover poly
reduced_polynomials = []
for i in range(nrows):
g_l = 0
for j in range(ncols):
g_l += L[i][j] // my_monomials[j](X, Y, Z) * my_monomials[j]
reduced_polynomials.append(g_l) # eliminate z
my_ideal_list = [y * z - N] + reduced_polynomials # Variety
my_ideal_list = [Hi.change_ring(QQ) for Hi in my_ideal_list]
for i in range(len(my_ideal_list),3,-1):
print(i)
V = Ideal(my_ideal_list[:i]).variety(ring=ZZ)
print(V)

参考:

New Attacks on RSA with Small Secret CRT-Exponents

NCTF 2022 - dp_promax

给p,q,dp,dq,c

import gmpy2 as gp

p =
q =
dp =
dq =
c = n = p*q
phin = (p-1)*(q-1)
dd = gp.gcd(p-1, q-1)
d=(dp-dq)//dd * gp.invert((q-1)//dd, (p-1)//dd) * (q-1) +dq
print(d) m = gp.powmod(c, d, n)
print('-------------------')
print(m)
print(hex(m)[2:])
print(bytes.fromhex(hex(m)[2:]))

给e,d,n

import random
import gmpy2 def divide_pq(e, d, n):
k = e*d - 1
while True:
g = random.randint(2, n-1)
t = k
while True:
if t % 2 != 0:
break
t //= 2
x = pow(g, t, n)
if x > 1 and gmpy2.gcd(x-1, n) > 1:
p = gmpy2.gcd(x-1, n)
return (p, n//p) p, q = divide_pq(e, d, n)
print(f'p = {p}')
print(f'q = {q}')
import random
import gmpy2 def factor_with_kphi(n, kphi):
t = 0
while kphi % 2 == 0:
kphi >>= 1
t += 1
for i in range(1, 101):
g = random.randint(0, n)
y = pow(g, kphi, n)
if y == 1 or y == n - 1:
continue
else:
for j in range(1, t):
x = pow(y, 2, n)
if x == 1:
p = gcd(n, y-1)
q = n//p
return p, q
elif x == n - 1:
continue
y = x
x = pow(y, 2, n)
if x == 1:
p = gcd(n, y-1)
q = n//p
return p, q

低解密指数攻击/低私钥指数攻击(e长度较大,d小,Wiener Attack)

RSAWienerHacker工具:https://github.com/pablocelayes/rsa-wiener-attack

#脚本1
#Sage
def factor_rsa_wiener(N, e):
N = Integer(N)
e = Integer(e)
cf = (e / N).continued_fraction().convergents()
for f in cf:
k = f.numer()
d = f.denom()
if k == 0:
continue
phi_N = ((e * d) - 1) / k
b = -(N - phi_N + 1)
dis = b ^ 2 - 4 * N
if dis.sign() == 1:
dis_sqrt = sqrt(dis)
p = (-b + dis_sqrt) / 2
q = (-b - dis_sqrt) / 2
if p.is_integer() and q.is_integer() and (p * q) % N == 0:
p = p % N
q = q % N
if p > q:
return (p, q)
else:
return (q, p)
#脚本2
#Sage
def rational_to_contfrac(x,y):
# Converts a rational x/y fraction into a list of partial quotients [a0, ..., an]
a = x // y
pquotients = [a]
while a * y != x:
x, y = y, x - a * y
a = x // y
pquotients.append(a)
return pquotients def convergents_from_contfrac(frac):
# computes the list of convergents using the list of partial quotients
convs = [];
for i in range(len(frac)): convs.append(contfrac_to_rational(frac[0 : i]))
return convs def contfrac_to_rational (frac):
# Converts a finite continued fraction [a0, ..., an] to an x/y rational.
if len(frac) == 0: return (0,1)
num = frac[-1]
denom = 1
for _ in range(-2, -len(frac) - 1, -1): num, denom = frac[_] * num + denom, num
return (num, denom) n =
e =
c = def egcd(a, b):
if a == 0: return (b, 0, 1)
g, x, y = egcd(b % a, a)
return (g, y - (b // a) * x, x) def mod_inv(a, m):
g, x, _ = egcd(a, m)
return (x + m) % m def isqrt(n):
x = n
y = (x + 1) // 2
while y < x:
x = y
y = (x + n // x) // 2
return x def crack_rsa(e, n):
frac = rational_to_contfrac(e, n)
convergents = convergents_from_contfrac(frac) for (k, d) in convergents:
if k != 0 and (e * d - 1) % k == 0:
phi = (e * d - 1) // k
s = n - phi + 1
# check if x*x - s*x + n = 0 has integer roots
D = s * s - 4 * n
if D >= 0:
sq = isqrt(D)
if sq * sq == D and (s + sq) % 2 == 0: return d d = crack_rsa(e, n)
m = hex(pow(c, d, n))[2:]
print(bytes.fromhex(m))
#脚本3
from Crypto.Util.number import long_to_bytes
e =
n =
c = #将分数x/y展开为连分数的形式
def transform(x,y):
arr=[]
while y:
arr+=[x//y]
x,y=y,x%y
return arr #求解渐进分数
def sub_fraction(k):
x=0
y=1
for i in k[::-1]:
x,y=y,x+i*y
return (y,x)
data=transform(e,n) for x in range(1,len(data)+1):
data1=data[:x]
d = sub_fraction(data1)[1]
m = pow(c,d,n)
flag = long_to_bytes(m)
if b'flag{' in flag:
print(flag)
break

变种1

参考:2020年羊城杯 - RRRRRRRSA

Paper: https://eprint.iacr.org/2015/399.pdf

连分数逼近

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 wienerAttack(n1,n2):
for (q2,q1) in sub_fraction(n1,n2): #用一个for循环来注意试探n1/n2的连续函数的渐进分数,直到找到一个满足条件的渐进分数
if q1==0: #可能会出现连分数的第一个为0的情况,排除
continue
if n1%q1==0 and q1!=1: #成立条件
return (q1,q2)
print("该方法不适用") N1=
N2=
print(wienerAttack(N1,N2))

变种2:

参考:Wiener’s v.s Lattices —— Ax≡y(mod P)的方程解法笔记

变种3:

参考:New Attacks on RSA with Modulus N = p^2q Using Continued Fractions

低加密指数广播攻击(Hastad攻击)

#sage
def chinese_remainder(modulus, remainders):
Sum = 0
prod = reduce(lambda a, b: a*b, modulus)
for m_i, r_i in zip(modulus, remainders):
p = prod // m_i
Sum += r_i * (inverse_mod(p,m_i)*p)
return Sum % prod
chinese_remainder([3,5,7],[2,3,2]) #23
#sage
crt([2,3,2],[3,5,7])

共模攻击(n,m相同,c,e不同)

import gmpy2 as gp
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y) n =
c1 =
c2 =
e1 =
e2 =
s = egcd(e1, e2)
s1 = s[1]
s2 = s[2]
if s1<0:
s1 = - s1
c1 = gp.invert(c1, n)
elif s2<0:
s2 = - s2
c2 = gp.invert(c2, n) m = pow(c1,s1,n)*pow(c2,s2,n) % n
print(hex(m)[2:])
print(bytes.fromhex(hex(m)[2:]))

e,m相同,多个n中存在两个n有GCD(模不互素)

import gmpy2 as gp

n=[]
for i in n:
for j in n:
if (i<>j):
pub_p=gp.gcdext(i,j)
if (pub_p[0]<>1)&(i>j):
print(i)
print(j)
print(pub_p[0])
a=i,p=pub_p[0]
q=a//p
p =
q =
e =
c =
n = p*q
phi = (p-1) * (q-1)
d = gp.invert(e, phi)
m = pow(c, d, n)
print(hex(m)[2:])
print(bytes.fromhex(hex(m)[2:]))

Rabin加密

import gmpy2

def rabin_decrypt(c, p, q, e=2):
n = p * q
mp = pow(c, (p + 1) // 4, p)
mq = pow(c, (q + 1) // 4, q)
yp = gmpy2.invert(p, q)
yq = gmpy2.invert(q, p)
r = (yp * p * mq + yq * q * mp) % n
rr = n - r
s = (yp * p * mq - yq * q * mp) % n
ss = n - s
return (r, rr, s, ss) c =
p =
q =
m = rabin_decrypt(c,p,q)
for i in range(4):
try:
print(bytes.fromhex(hex(m[i])[2:]))
except:
pass

Boneh and Durfee attack

参考 RSA-and-LLL-attacks

变种1

光滑数

p-1 光滑

from gmpy2 import *
a = 2
k = 2
N =
while True:
a = powmod(a, k, N)
res = gcd(a-1, N)
if res != 1 and res != N:
q = N // res
print("p =",res)
print("q =",q)
break
k += 1

p+1 光滑

William’s p+1分解算法。

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

Coppersmith攻击(已知p的高位攻击)

#Sage
n =
p4 = #p去0的剩余位
pbits = 1024
kbits = pbits - p4.nbits()
print(p4.nbits())
p4 = p4 << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + p4
roots = f.small_roots(X=2^kbits, beta=0.4)
if roots:
p = p4 + int(roots[0])
q = n//p
print(f'n: {n}')
print(f'p: {p}')
print(f'q: {q}')

Coppersmith攻击(已知m的高位攻击)

这里我们假设我们首先加密了消息 m,如下

可以参考 https://github.com/mimoo/RSA-and-LLL-attacks

#Sage
n =
e =
c =
mbar =
kbits =
beta = 1
nbits = n.nbits()
print("upper {} bits of {} bits is given".format(nbits - kbits, nbits))
PR.<x> = PolynomialRing(Zmod(n))
f = (mbar + x)^e - c
x0 = f.small_roots(X=2^kbits, beta=1)[0] # find root < 2^kbits with factor = n
print("m:", mbar + x0)

Coppersmith攻击(已知d的低位攻击)

#Sage
def partial_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.4) # find root < 2^(nbits//2-kbits) with factor >= n^0.4
if roots:
x0 = roots[0]
p = gcd(2^kbits*x0 + p0, n)
return ZZ(p)
def find_p(d0, kbits, e, n):
X = var('X')
for k in range(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 and p != 1:
return p
if __name__ == '__main__':
n =
e =
c =
d0 =
beta = 0.5
nbits = n.nbits()
kbits = d0.nbits()
print("lower %d bits (of %d bits) is given" % (kbits, nbits))
p = int(find_p(d0, kbits, e, n))
print("found p: %d" % p)
q = n//int(p)
print("d:", inverse_mod(e, (p-1)*(q-1)))

变种1

参考:Dragon CTF 2019 - RSA Chained

#Sage
def find_p(d0, kbits, e, n, p):
X = var('X')
for k in range(1, e + 1):
k_dot = k * (p - 1)
results = solve_mod([e * d0 * X - k_dot * X * (n - X + 1) + k_dot * n == X], 2^kbits)
for x in results:
q = ZZ(x[0])
if n % q == 0:
return q
return None n = ... # q * r
p =
c =
d0 =
e =
kbits = d0.nbits()
q = find_p(d0, kbits, e, n, p)
phi = (p - 1) * (q - 1) * (n // q - 1)
d = inverse_mod(e, phi)
print(bytes.fromhex(hex(pow(c, d, p * n))[2:]))

Coppersmith攻击(已知N一个因子的高位,部分p)

当我们知道一个公钥中模数 N的一个因子的较高位时,我们就有一定几率来分解 N。

参考 https://github.com/mimoo/RSA-and-LLL-attacks

关注下面的代码:

beta = 0.5
dd = f.degree()
epsilon = beta / 7
mm = ceil(beta**2 / (dd * epsilon))
tt = floor(dd * mm * ((1/beta) - 1))
XX = ceil(N**((beta**2/dd) - epsilon)) + 1000000000000000000000000000000000
roots = coppersmith_howgrave_univariate(f, N, beta, mm, tt, XX)

其中,

#Sage
n =
e =
c =
pbar =
kbits =
print("upper %d bits (of %d bits) is given" % (pbar.nbits()-kbits, pbar.nbits()))
PR.<x> = PolynomialRing(Zmod(n))
f = x + pbar
x0 = f.small_roots(X=2^kbits, beta=0.4)[0] # find root < 2^kbits with factor >= n^0.4
p = x0 + pbar
print("p:", p)
q = n // int(p)
d = inverse_mod(e, (p-1)*(q-1))
print("m:", pow(c, d, n))

注:

Coppersmith’s Short-pad Attack & Related Message Attack(Franklin-Reiter攻击)

#脚本1
#Sage
import binascii
def attack(c1, c2, n, e):
PR.<x>=PolynomialRing(Zmod(n))
# replace a,b,c,d
g1 = (a*x+b)^e - c1
g2 = (c*x+d)^e - c2 def gcd(g1, g2):
while g2:
g1, g2 = g2, g1 % g2
return g1.monic()
return -gcd(g1, g2)[0]
c1 =
c2 =
n =
e =
m1 = attack(c1, c2, n, e)
print(binascii.unhexlify("%x" % int(m1)))
#脚本2
#Sage
def short_pad_attack(c1, c2, e, n):
PRxy.<x,y> = PolynomialRing(Zmod(n))
PRx.<xn> = PolynomialRing(Zmod(n))
PRZZ.<xz,yz> = PolynomialRing(Zmod(n))
g1 = x^e - c1
g2 = (x+y)^e - c2
q1 = g1.change_ring(PRZZ)
q2 = g2.change_ring(PRZZ)
h = q2.resultant(q1)
h = h.univariate_polynomial()
h = h.change_ring(PRx).subs(y=xn)
h = h.monic()
kbits = n.nbits()//(2*e*e)
diff = h.small_roots(X=2^kbits, beta=0.4)[0] # find root < 2^kbits with factor >= n^0.4
return diff
def related_message_attack(c1, c2, diff, e, n):
PRx.<x> = PolynomialRing(Zmod(n))
g1 = x^e - c1
g2 = (x+diff)^e - c2
def gcd(g1, g2):
while g2:
g1, g2 = g2, g1 % g2
return g1.monic()
return -gcd(g1, g2)[0]
if __name__ == '__main__':
n =
e =
c1 =
c2 =
diff = short_pad_attack(c1, c2, e, n)
print("difference of two messages is %d" % diff)
m1 = related_message_attack(c1, c2, diff, e, n)
print("m1:", m1)
print("m2:", m1 + diff)

变种1:

参考:

Security Fest 2022 - really_sick_æsthetic

PlaidCTF 2017 - Multicast

CakeCTF 2021 - Party Ticket

RSA Hastad Attack with non-linear padding and different public keys(带非线性padding和不同公钥的广播攻击)

参考:2020年羊城杯 - Invitation

#Sage
#e=3, padding: m²+(3^431)k
def linearPaddingHastads(cArray,nArray,aArray,bArray,eArray,eps):
if(len(cArray) == len(nArray) == len(aArray) == len(bArray) == len(eArray)):
for i in range(4):
cArray[i] = Integer(cArray[i])
nArray[i] = Integer(nArray[i])
aArray[i] = Integer(aArray[i])
bArray[i] = Integer(bArray[i])
eArray[i] = Integer(eArray[i])
TArray = [-1]*4
for i in range(4):
arrayToCRT = [0]*4
arrayToCRT[i] = 1
TArray[i] = crt(arrayToCRT,nArray)
P.<x> = PolynomialRing(Zmod(prod(nArray)))
gArray = [-1]*4
for i in range(4):
gArray[i] = TArray[i]*(pow(aArray[i]*x**2 + bArray[i],eArray[i]) - cArray[i])
g = sum(gArray)
g = g.monic()
roots = g.small_roots(epsilon=eps)
if(len(roots)== 0):
print("No Solutions found!")
return -1
return roots
else:
print("Input error!") def nonLinearPadding():
eArr = [3 for i in range(4)]
nArr = []
cArr = []
aArr = [1 for i in range(4)]
bArr = [i * 3 ** 431 for i in [3,8,10,11]]
msg = linearPaddingHastads(cArr,nArr,aArr,bArr,eArr,eps=1/20)
for i in msg:
print(bytes.fromhex(hex(i)[2:])) if __name__ == '__main__':
nonLinearPadding()

选择明/密文攻击

选择明文攻击

import gmpy2

def get_n():
nset = []
c2 = server_encode(2)
c4 = server_encode(4)
c8 = server_encode(8)
nset.append(c2 * c2 - c4)
nset.append(c2 * c2 * c2 - c8)
c3 = server_encode(3)
c9 = server_encode(9)
c27 = server_encode(27)
nset.append(c3 * c3 - c9)
nset.append(c3 * c3 * c3 - c27)
c5 = server_encode(5)
c25 = server_encode(25)
c125 = server_encode(125)
nset.append(c5 * c5 - c25)
nset.append(c5 * c5 * c5 - c125)
n = nset[0]
for x in nset:
n = gmpy2.gcd(x, n)
while n % 2 == 0:
n //= 2
while n % 3 == 0:
n //= 3
while n % 5 == 0:
n //= 5
print('n =', n)
return n

选择密文攻击

from Crypto.Util.number import *

def get_M():
X = getPrime(5)
Y = (c * (X ** e)) % n
Z = server_decode(Y)
i = 0
while True:
M = (n * i + Z) // X
if 'flag' in long_to_bytes(M):
print(long_to_bytes(M))
break

Parity Oracle Attack

LSB Oracle Attack(Least Significant Bit Oracle Attack )

L = 0
R = n
for i in range(1024):
if b:
L = (L+R) // 2
else:
R = (L+R) // 2

由于此处有大量整除运算,所以最好用 decimal 库进行精确计算,否则最后结果很可能会出错。decimal.getcontext().prec 用来设定精度。

from Crypto.Util.number import *
import decimal def get_flag():
k = n.bit_length()
decimal.getcontext().prec = k
L = decimal.Decimal(0)
R = decimal.Decimal(int(n))
for i in range(k):
c = (c * pow(2, e, n)) % n
recv = server_decode(c)
if recv == 1:
L = (L + R) // 2
else:
R = (L + R) // 2
print(long_to_bytes(int((R))))

更多信息可参考:RSA Least-Significant-Bit Oracle AttackRSA least significant bit oracle attack

import decimal
def oracle():
return lsb == 'odd' def partial(c, e, n):
k = n.bit_length()
decimal.getcontext().prec = k # for 'precise enough' floats
lo = decimal.Decimal(0)
hi = decimal.Decimal(n)
for i in range(k):
if not oracle(c):
hi = (lo + hi) // 2
else:
lo = (lo + hi) // 2
c = (c * pow(2, e, n)) % n
# print i, int(hi - lo)
return int(hi)

MSB Oracle Attack(Most Significant Bit Oracle Attack )

参考:Pwnhub - pkcs4

Byte Oracle Attack

适用情况:可以选择密文并泄露最后一个字节。

这里:

假设服务器返回了 b,那么可以归纳为:

L = 0
R = 1
for i in range(128):
k = mab[b]
L = L + k // 256**(i+1)
R = L + (k+1)// 256**(i+1)
M = L * n

from Crypto.Util.number import *
from fractions import Fraction def get_flag():
map = {}
for i in range(0, 256):
map[-n * i % 256] = i cipher256 = server_encode(256)
backup = c L = Fraction(0, 1)
R = Fraction(1, 1)
for i in range(128):
c = c * cipher256 % n
b = server_decode(c)
k = map[b]
L, R = L + Fraction(k, 256**(i+1)), L + Fraction(k+1, 256**(i+1))
m = int(L * n)
print(long_to_bytes(m - m % 256 + server_decode(backup)))

Common Private Exponent(共私钥指数攻击,d相同)

加密用同样的私钥并且私钥比较短,从而导致了加密系统被破解。

假定:

Lattice Based Attack on Common Private Exponent RSA

SCTF 2020 - RSA

###Sage###
from gmpy2 import *
e0=
n0=
c0=
e1=
n1=
c1=
e2=
n2=
c2= M=iroot(int(n2),int(2))[0]
a=[0]*4
a[0]=[M,e0,e1,e2]
a[1]=[0,-n0,0,0]
a[2]=[0,0,-n1,0]
a[3]=[0,0,0,-n2] Mat = matrix(ZZ,a)
Mat_LLL=Mat.LLL()
d = abs(Mat_LLL[0][0])/M
print(bytes.fromhex(hex(pow(c1,int(d),int(n1)))[2:]))

多组低解密指数攻击

(g + k1s)(g + k2s)]

 from sage.all import *
import gmpy2
N =
e1 =
e2 =
c =
for i in range(1000):
alpha2 = i/1000
M1 = int(gmpy2.mpz(N)**0.5)
M2 = int( gmpy2.mpz(N)**(1+alpha2) )
D = diagonal_matrix(ZZ, [N, M1, M2, 1])
B = Matrix(ZZ, [ [1, -N, 0, N**2],
[0, e1, -e1, -e1*N],
[0, 0, e2, -e2*N],
[0, 0, 0, e1*e2] ]) * D
L = B.LLL()
v = Matrix(ZZ, L[0])
x = v * B**(-1)
phi = (x[0,1]/x[0,0]*e1).floor()
try:
d = inverse_mod( 65537, phi)
m = hex(power_mod(c, d, N))[2:]
if m.startswith('44415343'):
print(i)
print(bytes.fromhex(m))
break
except:
pass

参考:De1CTF 2020 - easyRSA

参考:3kCTF - RSA Textbook

from sage.all import *
import gmpy2 N =
e1 =
e2 =
e3 =
c = for i in range(1000):
alpha2 = i/1000
M1 = int(gmpy2.mpz(N)**(3./2))
M2 = int( gmpy2.mpz(N) )
M3 = int(gmpy2.mpz(N)**(3./2 + alpha2))
M4 = int( gmpy2.mpz(N)**(0.5) )
M5 = int( gmpy2.mpz(N)**(3./2 + alpha2) )
M6 = int( gmpy2.mpz(N)**(1.+alpha2) )
M7 = int( gmpy2.mpz(N)**(1.+alpha2) )
D = diagonal_matrix(ZZ, [M1, M2, M3, M4, M5, M6, M7, 1])
B = Matrix(ZZ, [ [1, -N, 0, N**2, 0, 0, 0, -N**3],
[0, e1, -e1, -e1*N, -e1, 0, e1*N, e1*N**2],
[0, 0, e2, -e2*N, 0, e2*N, 0, e2*N**2],
[0, 0, 0, e1*e2, 0, -e1*e2, -e1*e2, -e1*e2*N],
[0, 0, 0, 0, e3, -e3*N, -e3*N, e3*N**2],
[0, 0, 0, 0, 0, e1*e3, 0, -e1*e3*N],
[0, 0, 0, 0, 0, 0, e2*e3, -e2*e3*N],
[0, 0, 0, 0, 0, 0, 0, e1*e2*e3] ]) * D L = B.LLL() v = Matrix(ZZ, L[0])
x = v * B**(-1)
phi_ = (e1*x[0,1]/x[0,0]).floor()
try:
d = inverse_mod( 65537, phi_)
m = hex(power_mod(c, d, N))[2:]
if m.startswith('44415343'):
print(i)
print(bytes.fromhex(m))
break
except:
pass

给定更多组

from Crypto.Util.number import *

isdigit = lambda x: ord('0') <= ord(x) <= ord('9')
def my_permutations(g, n):
sub = []
res = []
def dfs(s, prev):
if len(s) == n:
res.append(s[::])
for i in g:
if i in s or i < prev:
continue
s.append(i)
dfs(s, max(prev, i))
s.remove(i)
dfs(sub, 0)
return res class X3NNY(object):
def __init__(self, exp1, exp2):
self.exp1 = exp1
self.exp2 = exp2 def __mul__(self, b):
return X3NNY(self.exp1 * b.exp1, self.exp2 * b.exp2) def __repr__(self):
return '%s = %s' % (self.exp1.expand().collect_common_factors(), self.exp2) class X_Complex(object):
def __init__(self, exp):
i = 0
s = '%s' % exp
while i < len(s):
if isdigit(s[i]):
num = 0
while i < len(s) and isdigit(s[i]):
num = num*10 + int(s[i])
i += 1
if i >= len(s):
self.b = num
elif s[i] == '*':
self.a = num
i += 2
elif s[i] == '/':
i += 1
r = 0
while i < len(s) and isdigit(s[i]):
r = r*10 + int(s[i])
i += 1
self.b = num/r
else:
i += 1
if not hasattr(self, 'a'):
self.a = 1
if not hasattr(self, 'b'):
self.b = 0 def WW(e, d, k, g, N, s):
return X3NNY(e*d*g-k*N, g+k*s)
def GG(e1, e2, d1, d2, k1, k2):
return X3NNY(e1*d1*k2- e2*d2*k1, k2 - k1) def W(i):
e = eval("e%d" % i)
d = eval("d%d" % i)
k = eval("k%d" % i)
return WW(e, d, k, g, N, s) def G(i, j):
e1 = eval("e%d" % i)
d1 = eval("d%d" % i)
k1 = eval("k%d" % i) e2 = eval("e%d" % j)
d2 = eval("d%d" % j)
k2 = eval("k%d" % j) return GG(e1, e2, d1, d2, k1, k2) def R(e, sn): # min u max v
ret = X3NNY(1, 1)
n = max(e)
nn = len(e)
l = set(i for i in range(1, n+1))
debug = ''
u, v = 0, 0
for i in e:
if i == 1:
ret *= W(1)
debug += 'W(%d)' % i
nn -= 1
l.remove(1)
u += 1
elif i > min(l) and len(l) >= 2*nn:
ret *= G(min(l), i)
nn -= 1
debug += 'G(%d, %d)' % (min(l), i)
l.remove(min(l))
l.remove(i)
v += 1
else:
ret *= W(i)
l.remove(i)
debug += 'W(%d)' % i
nn -= 1
u += 1
# print(debug, end = ' ')
return ret, u/2 + (sn - v) * a def H(n):
if n == 0:
return [0]
if n == 2:
return [(), (1,), (2,), (1, 2)]
ret = []
for i in range(3, n+1):
ret.append((i,))
for j in range(1, i):
for k in my_permutations(range(1, i), j):
ret.append(tuple(k + [i]))
return H(2) + ret def CC(exp, n):
cols = [0 for i in range(1<<n)] # split exp
texps = ('%s' % exp.exp1.expand()).strip().split(' - ')
ops = []
exps = []
for i in range(len(texps)):
if texps[i].find(' + ') != -1:
tmp = texps[i].split(' + ')
ops.append(0)
exps.append(tmp[0])
for i in range(1, len(tmp)):
ops.append(1)
exps.append(tmp[i])
else:
ops.append(0)
exps.append(texps[i])
if exps[0][0] == '-':
for i in range(len(exps)):
ops[i] = 1-ops[i]
exps[0] = exps[0][1:]
else:
ops[0] = 1
# find e and N
l = []
for i in range(len(exps)):
tmp = 1 if ops[i] else -1
en = []
j = 0
while j < len(exps[i]):
if exps[i][j] == 'e':
num = 0
j += 1
while isdigit(exps[i][j]):
num = num*10 + int(exps[i][j])
j += 1
tmp *= eval('e%d' % num)
en.append(num)
elif exps[i][j] == 'N':
j += 1
num = 0
if exps[i][j] == '^':
j += 1
while isdigit(exps[i][j]):
num = num*10 + int(exps[i][j])
j += 1
if num == 0:
num = 1
tmp *= eval('N**%d' % num)
else:
j += 1
if tmp == 1 or tmp == -1:
l.append((0, ()))
else:
l.append((tmp, tuple(sorted(en)))) # construct h
mp = H(n)
for val, en in l:
cols[mp.index(en)] = val
#print(cols)
return cols def EWA(n, elist, NN, alpha):
mp = H(n)
var('a')
S = [X_Complex(n*a)]
cols = [[1 if i == 0 else 0 for i in range(2^n)]]
for i in mp[1:]:
eL, s = R(i, n)
cols.append(CC(eL, n))
S.append(X_Complex(s)) alphaA,alphaB = 0, 0
for i in S:
alphaA = max(i.a, alphaA)
alphaB = max(i.b, alphaB)
#print(alphaA, alphaB)
D = [int(NN^((alphaA-S[i].a)*alpha + (alphaB - S[i].b))) for i in range(len(S))]
#print(D)
kw = {'N': NN}
for i in range(len(elist)):
kw['e%d' % (i+1)] = elist[i]
print(cols)
B = Matrix(ZZ, Matrix(cols).T(**kw)) * diagonal_matrix(ZZ, D)
L = B.LLL(0.5)
v = Matrix(ZZ, L[0])
x = v * B**(-1)
phi = int(x[0,1]/x[0,0]*elist[0])
return phi def attack(NN, elist, alpha):
phi = EWA(len(elist), elist, NN, alpha)
print(phi) elist = [ ]
NN=
alpha = 400./2048
for i in range(1, len(elist)+1):
var("e%d" % i)
var("d%d" % i)
var("k%d" % i)
g, N, s = var('g'), var('N'), var('s') for i in range(len(elist)):
elist[i] = Integer(elist[i])
attack(NN, elist, alpha)
phi =
c=
d=inverse(65537,int(phi))
m = pow(c,d,NN)
print(long_to_bytes(int(m)))

参考:西湖论剑 2021 - WienerStudyTwice

参考Paper:

Common Modulus Attacks on Small Private Exponent RSA and Some Fast Variants (in Practice)

Extending Wiener’s Attack in the Presence of Many Decrypting Exponents

多项式RSA

#脚本1
#Sage
#已知p,n,m^e
p=
P = PolynomialRing(Zmod(p), name = 'x')
x = P.gen()
e =
n =
c = #分解N
q1, q2 = n.factor()
q1, q2 = q1[0], q2[0] #求φ,注意求法,
phi = (p**q1.degree() - 1) * (p**q2.degree() - 1)
assert gcd(e, phi) == 1
d = inverse_mod(e, phi)
m = pow(c,d,n) #取多项式系数
flag = bytes(m.coefficients())
print("Flag: ", flag.decode())
#脚本2
#Sage
#已知p=2,n,e,c
p =
P = PolynomialRing(GF(p), name = 'x')
x = P.gen()
e =
n =
R.<a> = GF(2^2049)
c = [] q1, q2 = n.factor()
q1, q2 = q1[0], q2[0] phi = (p**q1.degree() - 1) * (p**q2.degree() - 1)
assert gcd(e, phi) == 1
d = inverse_mod(e, phi) ans = ''
for cc in c:
cc = P(R.fetch_int(cc))
m = pow(cc,d,n)
m = R(P(m)).integer_representation()
print(m)
ans += chr(m)
print(ans)
#Sage
#x.nbits()==2^32
poly = sum(e * x^i for i,e in enumerate(Integer(n).digits(2^32)))
(p, _), (q, _) = poly.factor_list()
p, q = p(x=2^32), q(x=2^32)

参考:

0ctf - babyrsa

watevrCTF 2019 - Swedish RSA

InCTF 2020 - PolyRSA

Polynomial based RSA

Crypto CTF2020 - Decent RSA

SecurityFest CTF 2022 - small rsa

Weak prime factors (p具线性特征)

参考:

Factoring RSA moduli with weak prime factors

N1CTF2020 - easyRSA

from tqdm import tqdm
import gmpy2 class success(Exception):
pass def attack_weak_prime(basenum, exp, n):
m = basenum^exp
k = len(n.str(base=basenum))//(2*exp) + 1
c = gmpy2.iroot(2*k^3, int(2))
# assert c[1] == True
tmp = int(c[0]) try:
for c in tqdm(range(1, tmp)):
amount = 2*k+1 M = Matrix(RationalField(), amount, amount)
for i in range(amount):
M[i, i] = 1
M[i, amount-1] = c*m^(2*k-i)
M[amount-1, amount-1] = -c*n new_basis = M.LLL(delta=0.75)
for j in range(amount):
last_row = list(new_basis[j])
last_row[-1] = last_row[-1]//(-c) poly = sum(e * x^(k*2-i) for i,e in enumerate(last_row))
fac = poly.factor_list()
if len(fac) == 2:
p_poly, q_poly = fac
p_coefficient = p_poly[0].list()
q_coefficient = q_poly[0].list()
ap = sum(m^i * j for i,j in enumerate(p_coefficient))
bq = sum(m^i * j for i,j in enumerate(q_coefficient))
p = gcd(ap, n)
q = gcd(bq, n) if (p*q == n) and (p != 1) and (q != 1):
raise success except:
print ('n =', n)
print ('p =', p)
print ('q =', q)
print ('p*q == n ?', bool(p*q == n)) if __name__ == '__main__':
print ('[+] Weak Prime Factorization Start!')
print ('-------------------------------------------------------------------------------------------------------------------------------')
basenum, exp = (3, 66)
n = 32846178930381020200488205307866106934814063650420574397058108582359767867168248452804404660617617281772163916944703994111784849810233870504925762086155249810089376194662501332106637997915467797720063431587510189901

p多次幂因子

P.<x> = PolynomialRing(Zmod(n))
f = e * x - b
root = f.monic().small_roots(X=2**672,beta=0.75)[0]
g = gcd(int(e * root - b),n3)

情形2

P.<x> = PolynomialRing(Zmod(n))
f = e1*e2*x - e1 + e2
root = f.monic().small_roots(X=2**672,beta=0.75)[0]
g = gcd(int(e1*e2*root - e1 + e2),n)

情形3

cf = continued_fraction(n1/n2)
fracs = cf.convergents()
for xx in tqdm(fracs):
q1 = xx.numerator()
q2 = xx.denominator()
if q1.nbits() in range(511, 513) and q2.nbits() in range(511, 513):
if n1 % q1 == 0:
print(q1)
assert n1 % q1 == 0
p1 = int((n1 // q1)^(1/2))
p2 = int((n2 // q2)^(1/2))
assert p1^2 * q1 == n1
break

参考:

https://eprint.iacr.org/2015/399.pdf

D^3CTF 2022 - d3factor

RSA-CRT

故障攻击

错误模攻击

from tqdm import tqdm
import gmpy2,sys def orthogonal_lattice(B):
LB = B.transpose().left_kernel(basis="LLL").basis_matrix()
return LB cs = []
s = []
l = 6 v = []
for i in range(len(cs_)):
v.append(int(crt([s_[i], cs_[i]], [n, N]))) v = vector(ZZ, v)
Lv = orthogonal_lattice(Matrix(v))
L1 = orthogonal_lattice(Lv.submatrix(0, 0, l-2, l))
x, y = L1
for a in tqdm(range(333)):
for b in tqdm(range(333)):
z = a*x+b*y
for each in (v-z):
tmp = gcd(each,n)
if tmp>1:
p = tmp
print(p)
sys.exit()

参考:

Modulus Fault Attacks Against RSA-CRT Signatures

2022巅峰极客 - Learning with Fault

其他特别情形

多素数因子(Multi-prime RSA)

next_prime()

费马因式分解

给 e,p,c

给 e,d,modinv(q,p),c

gcd(e,φ(n)) ≠ 1

参考:

De1CTF2019 - Baby RSA

0ctf 2016 - RSA?

e|(p-1), e|(q-1)

参考:NCTF 2019 - easyRSAAdleman-Manders-Miller rth Root Extraction Method

这种情况下,开平方根可以用Tonelli–Shanks algorithmWiki说这个算法可以扩展到开n次方根

在这篇paper里给出了具体的算法:Adleman-Manders-Miller rth Root Extraction Method

StackOverflow – Cube root modulo P 给出了方法。

如何找到所有的primitive 0x1337th root of 1?

StackExchange – Finding the n-th root of unity in a finite field 给出了方法。

#脚本1
#Sage
c =
p =
q =
e = for mp in GF(p)(c).nth_root(e, all=True):
for mq in GF(q)(c).nth_root(e, all=True):
m = crt([ZZ(mp), ZZ(mq)], [p, q])
try:
res = bytes.fromhex(hex(m)[2:])
if res.isascii():
print(res)
except:
pass
#脚本2
#Sage
import random
import time # About 3 seconds to run
def AMM(o, r, q):
start = time.time()
print('\n----------------------------------------------------------------------------------')
print('Start to run Adleman-Manders-Miller Root Extraction Method')
print('Try to find one {:#x}th root of {} modulo {}'.format(r, o, q))
g = GF(q)
o = g(o)
p = g(random.randint(1, q))
while p ^ ((q-1) // r) == 1:
p = g(random.randint(1, q))
print('[+] Find p:{}'.format(p))
t = 0
s = q - 1
while s % r == 0:
t += 1
s = s // r
print('[+] Find s:{}, t:{}'.format(s, t))
k = 1
while (k * s + 1) % r != 0:
k += 1
alp = (k * s + 1) // r
print('[+] Find alp:{}'.format(alp))
a = p ^ (r**(t-1) * s)
b = o ^ (r*alp - 1)
c = p ^ s
h = 1
for i in range(1, t):
d = b ^ (r^(t-1-i))
if d == 1:
j = 0
else:
print('[+] Calculating DLP...')
j = - discrete_log(a, d)
print('[+] Finish DLP...')
b = b * (c^r)^j
h = h * c^j
c = c ^ r
result = o^alp * h
end = time.time()
print("Finished in {} seconds.".format(end - start))
print('Find one solution: {}'.format(result))
return result def findAllPRoot(p, e):
print("Start to find all the Primitive {:#x}th root of 1 modulo {}.".format(e, p))
start = time.time()
proot = set()
while len(proot) < e:
proot.add(pow(random.randint(2, p-1), (p-1)//e, p))
end = time.time()
print("Finished in {} seconds.".format(end - start))
return proot def findAllSolutions(mp, proot, cp, p):
print("Start to find all the {:#x}th root of {} modulo {}.".format(e, cp, p))
start = time.time()
all_mp = set()
for root in proot:
mp2 = mp * root % p
assert(pow(mp2, e, p) == cp)
all_mp.add(mp2)
end = time.time()
print("Finished in {} seconds.".format(end - start))
return all_mp c =
p =
q =
e = 0x1337
cp = c % p
cq = c % q
mp = AMM(cp, e, p)
mq = AMM(cq, e, q)
p_proot = findAllPRoot(p, e)
q_proot = findAllPRoot(q, e)
mps = findAllSolutions(mp, p_proot, cp, p)
mqs = findAllSolutions(mq, q_proot, cq, q)
print(mps, mqs) def check(m):
h = m.hex()
if len(h) & 1:
return False
if bytes.fromhex(h).startswith(b'NCTF'):
print(bytes.fromhex(h))
return True
else:
return False # About 16 mins to run 0x1337^2 == 24196561 times CRT
start = time.time()
print('Start CRT...')
for mpp in mps:
for mqq in mqs:
solution = CRT_list([int(mpp), int(mqq)], [p, q])
if check(solution):
print(solution)
print(time.time() - start) end = time.time()
print("Finished in {} seconds.".format(end - start))

SMUPE 问题(不同N,e加密线性关系明文)

a system of univariate polynomial equations problem = 一元多项式方程组求解问题

参考:2019红帽杯 - 精明的Alice

反素数(emirp数)

参考:

ASIS 2015 Finals: RSASR

Midnight Sun CTF 2020 Quals

RoarCTF 2020 - Reverse

#python2
#x=10
n = 6528060431134312098979986223024580864611046696815854430382374273411300418237131352745191078493977589108885811759425485490763751348287769344905469074809576433677010568815441304709680418296164156409562517530459274464091661561004894449297362571476259873657346997681362092440259333170797190642839587892066761627543
def t(a, b, k):
# sqrt(n) has 155 digits, so we need to figure out 77 digits on each side
if k == 77:
if a*b == n:
print a, b
return
for i in xrange(10):
for j in xrange(10):
# we try to guess the last not-already-guessed digits of both primes
a1 = a + i*(10**k) + j*(10**(154-k))
b1 = b + j*(10**k) + i*(10**(154-k))
if a1*b1 > n:
# a1 and b1 are too large
continue
if (a1+(10**(154-k)))*(b1+(10**(154-k))) < n:
# a1 and b1 are too small
continue
if ((a1*b1)%(10**(k+1))) != (n%(10**(k+1))):
# The last digits of a1*b1 (which won't change later) doesn't match n
continue
# this a1 and b1 seem to be a possible match, try to guess remaining digits
t(a1, b1, k+1) # the primes have odd number of digits (155), so we try all possible middle digits (it simplifies the code)
for i in xrange(10):
t(i*(10**77), i*(10**77), 0)

4p-1 method

对使用一类特定素数乘积的模数的分解。

import sys

sys.setrecursionlimit(10^6)

def QiCheng(n):
R = Integers(n)
attempts = 20
js = [0, (-2^5)^3, (-2^5*3)^3, (-2^5*3*5)^3, (-2^5*3*5*11)^3, (-2^6*3*5*23*29)^3] for _ in range(attempts):
for j in js:
if j == 0:
a = R.random_element()
E = EllipticCurve([0, a]) else:
a = R(j)/(R(1728)-R(j))
c = R.random_element()
E = EllipticCurve([3*a*c^2, 2*a*c^3]) x = R.random_element()
z = E.division_polynomial(n, x)
g = gcd(z, n)
if g > 1:
return g n =
p = int(QiCheng(Integer(n)))

CM-based factorization

参考:

浅谈 QiCheng Prime

NCTF 2020 - RSA_revenge

CryptoHack Challenge - RSA Backdoor Viability

Common Prime RSA

# Pollard’s rho
from Crypto.Util.number import *
import gmpy2 def f(x, n):
return (pow(x, n - 1, n) + 3) % n
def rho(n):
i = 1
print 'Factorizing'
while True:
x1 = getRandomRange(2, n)
x2 = f(x1, n)
j = 1
while True:
p = gmpy2.gcd(abs(x1 - x2), n)
if p == n:
break
elif p > 1 and isPrime(p):
print 'Found!'
return (p, n // p)
else:
x1 = f(x1, n)
x2 = f(f(x2, n), n)
j += 1
i += 1

参考:Common Prime RSA 笔记

RSA Padding Oracle Attack

参考:

Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard PKCS #1

Bleichenbachers “Million Message Attack” on RSA

Pwnhub - pkcs_fix

Return of Coppersmith’s attack (ROCA)

https://github.com/jvdsn/crypto-attacks/blob/master/attacks/factorization/roca.py

https://github.com/FlorianPicca/ROCA

PEM密钥

-----BEGIN <TAG>-----开头,-----END <TAG>-----结尾,中间是Base64编码的一串二进制,每64个字母(即解码后的48bytes)有一个换行。中间的Base64解码后是一串遵循ASN.1协议的DER编码,简单来说可以看成一种序列化,把一个结构体中的整数、字串等编码成一个方便传输的二进制。

生成代码:

from Crypto.PublicKey import RSA

rsa = RSA.generate(1024)
pk = rsa.publickey().exportKey()
sk = rsa.exportKey() with open ('./pub.pem', 'wb') as f:
f.write(pk) with open ('./priv.pem', 'wb') as f:
f.write(sk)

RSA私钥

-----BEGIN RSA PRIVATE KEY-----
...Base64 encoded key...
-----END RSA PRIVATE KEY-----

RFC3447定义:

RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- (inverse of q) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
}
Version ::= INTEGER { two-prime(0), multi(1) }
(CONSTRAINED BY
{-- version must be multi if otherPrimeInfos present --})

例:

3082025d02010002818100a0d154d5bf97c40f7797b44819d09c608fa4b5c38e70d83bc13267138c6eff4c1aacefe3ddb571e1b41d911c7ab6136cf90493189563450e1f4270cabbc4207c54c4da7b84a20311cfbbabe82b9fe60bdf48a08d57839d0cdf9464d84262bcc06bc308095a6987f60ad07d669a312b5a7e4133213788eecf25863248b91349ef02030100010281800f8270c496903bf3e3ec4912450f15edc81cb1fcf4b154615aee11fbd428e64d402b5a8d66d5f770358f3e6df935b324e8d5349c83d7c992a5982249a31734acb1db19c4c8d829267514bc1ef7bbfbe242d4350f67a002a56d33e56d1a94adc71c68f020dc39ab7d0064c111b164e26ba0698dc94a03cdfd516ffd966e877949024100ca97e49c058237f96e99118ce383f91912cba1163de9236181ff754ef3ef1a260fac8d2d9aee866d51a8b6836983b05cf850e786289b6859925bc8695fc67c47024100cb3630aafffcb29607f0833dc7f05c143ee92fadfe975da4cf6719e71226bee72562e8631328a25d7351507a8d43c1295ab6ea242b60a28b109233a983f4211902401b4a32a541a8b4d988a85dd0d8a4e25d1a470bbfef3f0461121dd3337b706dd94aab37a9390180622169d48c071e921733ebd204245c2ac6460ccf0642bc7de90241008d9f44a7c823eaaa58fa2bdd20bcc8cf6b50c463f4acb51ca956e75c7ceff7d7cbdc74aca7ab880cacd39cccec2aae320e00b0896899be6e40ac43c8fe2763f1024100c67ca6d988f53abea82159431a146512a8d942978d4a8f83f2d426f1095e3bf1b5b9b8b1ccbbad2a31c6401880447a45f5e0790269061ac13b5f68f1777d7f07

30是Sequence的tag,82是指接下来后两个bytes是这个Sequence的长度,即0x025d个bytes,也就是剩下全部都是;接着的020100就是整数0,其中02是整数的tag,01是这个整数占1byte,00是value同样的方法也可以解02818100a0...和后面其他整数,拆分:

3082025d  	# Begin Sequence: len=0x025d

0201  		# Version: (len=0x01)
00 028181 # n: (len=0x81)
00a0d154d5bf97c40f7797b44819d09c608fa4b5c38e70d83bc13267138c6eff4c1aacefe3ddb571e1b41d911c7ab6136cf90493189563450e1f4270cabbc4207c54c4da7b84a20311cfbbabe82b9fe60bdf48a08d57839d0cdf9464d84262bcc06bc308095a6987f60ad07d669a312b5a7e4133213788eecf25863248b91349ef 0203 # e: (len=0x03)
010001 028180 # d: (len=0x80)
0f8270c496903bf3e3ec4912450f15edc81cb1fcf4b154615aee11fbd428e64d402b5a8d66d5f770358f3e6df935b324e8d5349c83d7c992a5982249a31734acb1db19c4c8d829267514bc1ef7bbfbe242d4350f67a002a56d33e56d1a94adc71c68f020dc39ab7d0064c111b164e26ba0698dc94a03cdfd516ffd966e877949 0241 # p: (len=0x41)
00ca97e49c058237f96e99118ce383f91912cba1163de9236181ff754ef3ef1a260fac8d2d9aee866d51a8b6836983b05cf850e786289b6859925bc8695fc67c47 0241 # q: (len=0x41)
00cb3630aafffcb29607f0833dc7f05c143ee92fadfe975da4cf6719e71226bee72562e8631328a25d7351507a8d43c1295ab6ea242b60a28b109233a983f42119 0240 # d mod (p-1): (len=0x40)
1b4a32a541a8b4d988a85dd0d8a4e25d1a470bbfef3f0461121dd3337b706dd94aab37a9390180622169d48c071e921733ebd204245c2ac6460ccf0642bc7de9 0241 # d mod (q-1): (len=0x41)
008d9f44a7c823eaaa58fa2bdd20bcc8cf6b50c463f4acb51ca956e75c7ceff7d7cbdc74aca7ab880cacd39cccec2aae320e00b0896899be6e40ac43c8fe2763f1 0241 # (inverse of q) mod p: (len=0x41)
00c67ca6d988f53abea82159431a146512a8d942978d4a8f83f2d426f1095e3bf1b5b9b8b1ccbbad2a31c6401880447a45f5e0790269061ac13b5f68f1777d7f07 # End Sequence

RSA公钥

-----BEGIN PUBLIC KEY-----
...Base64 encoded key...
-----END PUBLIC KEY-----

例:

30819f300d06092a864886f70d010101050003818d0030818902818100a0d154d5bf97c40f7797b44819d09c608fa4b5c38e70d83bc13267138c6eff4c1aacefe3ddb571e1b41d911c7ab6136cf90493189563450e1f4270cabbc4207c54c4da7b84a20311cfbbabe82b9fe60bdf48a08d57839d0cdf9464d84262bcc06bc308095a6987f60ad07d669a312b5a7e4133213788eecf25863248b91349ef0203010001

拆分:

30819f 		# Begin Main Sequence: len=0x9f

300d		# Begin Sub1 Sequence: len=0x0d

0609		# algo_oid: (1.2.840.113549.1.1.1  - PKCSv1.2)
2a864886f70d010101 0500 # params: (null) # End Sub1 Sequence 03818d # BitString: len=0x8d ([n, e]) 00308189 # Begin Sub2 Sequence: len=0x89 028181 # n:
00a0d154d5bf97c40f7797b44819d09c608fa4b5c38e70d83bc13267138c6eff4c1aacefe3ddb571e1b41d911c7ab6136cf90493189563450e1f4270cabbc4207c54c4da7b84a20311cfbbabe82b9fe60bdf48a08d57839d0cdf9464d84262bcc06bc308095a6987f60ad07d669a312b5a7e4133213788eecf25863248b91349ef 0203 # e:
010001 # End Sub2 Sequence # End Main Sequence

参考

手撕PEM密钥

详细原理

二十年以来对 RSA 密码系统攻击综述

CTF Wiki - RSA

0xDktb’s Blog

RSA常见攻击方法

Cryptanalysis of RSA and It’s Variants

RSA总结 From La神的更多相关文章

  1. c#与JavaScript实现对用户名、密码进行RSA非对称加密

    博主最近手上这个项目呢(就是有上百个万恶的复杂excel需要解析的那个项目,参见博客:http://www.cnblogs.com/csqb-511612371/p/4885930.html),由于是 ...

  2. C# 与JAVA 的RSA 加密解密交互,互通,C#使用BouncyCastle来实现私钥加密,公钥解密的方法

    因为C#的RSA加密解密只有公钥加密,私钥解密,没有私钥加密,公钥解密.在网上查了很久也没有很好的实现.BouncyCastle的文档少之又少.很多人可能会说,C#也是可以的,通过Biginteger ...

  3. 跨越千年的RSA算法

    转载自http://www.matrix67.com/blog/archives/5100 数论,数学中的皇冠,最纯粹的数学.早在古希腊时代,人们就开始痴迷地研究数字,沉浸于这个几乎没有任何实用价值的 ...

  4. LA 3295 (计数 容斥原理) Counting Triangles

    如果用容斥原理递推的办法,这道题确实和LA 3720 Highway很像. 看到大神们写的博客,什么乱搞啊,随便统计一下,这真的让小白很为难,于是我决定用比较严格的语言来写这篇题解. 整体思路很简单: ...

  5. 【推荐】Java工程师如何从普通成为大神值得一读

    本文源自 http://www.hollischuang.com/archives/489 一点感悟 java作为一门编程语言,在各类编程语言中作为弄潮儿始终排在前三的位置,这充分肯定了java语言的 ...

  6. 数据结构中La表的数据合并到Lb表中

    实验描述:La表中的数据为(3,5,8,11)  Lb 表中的数据为(2,6,8,9,11,15,20) 将La表中的数据而不存在Lb表的数据插入到Lb表中,从而实现并集操作. 出现的问题:最后实现的 ...

  7. 再谈加密-RSA非对称加密的理解和使用

    html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...

  8. RSA 算法

    RSA 算法  from http://www.matrix67.com/blog/archives/5100 所有工作都准备就绪,下面我们可以开始描述 RSA 算法了. 首先,找两个质数,比如说 1 ...

  9. 利用RSACryptoServiceProvider进行RSA加密解密

    前言: 本文只介绍How to use,对于加密算法的研究不予讨论. 关于私钥的存储,微软给的建议是使用windows自带的秘钥容器,相见文档. 为了直观看到私钥和公钥,本文直接将其存入XML文件中. ...

  10. 求大神帮解答calendar日期插件的问题

    小颖最近公司的项目里用了一款日期插件  calendar.js  但是在用的过程中遇到了难题,就是当日期只需要选择具体的月份就可以了,不需要再选具体日期时,小颖解决不了,只能让它默认显示出月份,但是月 ...

随机推荐

  1. hexo博客Yilia主题首页菜单中文乱码解决方案

    方案一: 菜单设置成中文显示,编辑博客根目录下的_config.yml文件 设置language字段如下: language: zh-Hans 或者 language: zh-CN 取决于你的主题th ...

  2. 2023年郑州轻工业大学校赛邀请赛jc

    比赛时,jxh和myh从头开始看题,我拿着试题册去找签到,很快他们签上了一个数学题,我跟他们说兔子和飞镖可以写,刚开始飞镖这个题我先wa了一次,因为刚开始的思路少考虑了情况,我们队后来改的挺乱,jxh ...

  3. Android 妙用TextView实现左边文字,右边图片

    原文: Android 妙用TextView实现左边文字,右边图片 - Stars-One的杂货小窝 有时候,需要文字在左边,右边有个箭头,我个人之前会有两种做法: 使用线性布局来实现 或者使用约束布 ...

  4. SkipList原理与实现

    机制 链表中查询的效率的复杂度是O(n), 有没有办法提升这个查询复杂度呢? 最简单的想法就是在原始的链表上构建多层索引. 在level 1(最底层为0), 每2位插入一个索引, 查询复杂度便是 O( ...

  5. HTML超文本标记语言4

    框架标签...等等 1.框架 <frameset> 框架标签 cols="按列划分" rows="按行划分" 格式:rows="100,* ...

  6. pip 更新

    pip install --user --upgrade pip成功升级

  7. 2023-07-31:用r、e、d三种字符,拼出一个回文子串数量等于x的字符串。 1 <= x <= 10^5。 来自百度。

    2023-07-31:用r.e.d三种字符,拼出一个回文子串数量等于x的字符串. 1 <= x <= 10^5. 来自百度. 答案2023-07-31: 大体步骤如下: 1.初始化一个字符 ...

  8. python3使用ESL和sipp自动多轮压测FreeSWITCH

    环境:CentOS 7.6_x64   FreeSWITCH版本 :1.10.9   sipp版本:3.6.1   python版本:3.9.12 日常工作中,有时会遇到批量自动压测FreeSWITC ...

  9. 6、Mybatis之高级查询

    6.1.创建接口.映射文件和测试类 ++++++++++++++++++++++++++分割线++++++++++++++++++++++++++ 注意namespace属性值为对应接口的全限定类名 ...

  10. cockpit--一款开源的适用于单主机的Linux监控面板

    在搜索Linux监控时,偶然发现一款还不错的监控面板,该面板为red hat开发,适用于各种Linux发行版,部署也非常方便,官方文档Running Cockpit - Cockpit Project ...