RSA:
RSA加密操作流程:
第一步:选取一对不相等且足够大的质数,记做p和q;
第二步:计算p和q的乘积n;n=pq;
第三步:计算欧拉函数:phi(n)=(p-1)(q-1)
第四步:选一个与phi(n)互素的整数e,1<e<phi(n)
第五步:计算出e对phi(n)的模反元素d,de mod phi(n)=1;
第六步:公钥—K1=(e,n);
第七步:私钥—K2=(d,n);
加密:明文M: M^e mod n = C
解密:密文C: C^d mod n = M
RSA普通破解方式
1.RSA安全位数:
RSA算法的安全性取决于其底层的数学难题—大整数因数分解问题的难度。目前最新的大整数分解的世界记录为829-bits().
2.直接分解:
对于一些不安全/位数比较小的素数(N),以当前的算法和算力很容易将其分解。通常在300bits以下的模数均可在较短的时间内被分解,一些不安全的素数也会很快被特定的算法所分解掉
CTF中常见的分解手法:
在线网站:factordb.com(类似于一个映射表,存放前人已经分解过的大素数的分解结果,然后根据你的输入进行查找)
在线网站:https://www.alpertron.con.com.ar/ECM.HTM(运行ECM分解算法来计算)
工具:yafu
SageMath内置的factor函数:
操作:
1.判断n的位数:
n=0x564656435135468436651321354368368154136547683543
n.bit_length()
2.求p和q
因为n的位数比较小直接通过在线网站可以获取到n的分解结果p和q
3.求解明文m
之后的步骤相当于已知p和q,c求明文m
#n,p和q已知可推出
#公钥e,密文c知道
from Crypto.Util.number import *
d=inverse(e,(p-1)*(q-1))
m = pow(c,d,n)
long_to_bytes(m)
print(long_to_bytes(m))
3.p,q相近:
在计算RSA模数的时候,生成两个素数靠的很近,这个时候也很不安全的。
例如生成:
生成代码:
p和q相近的情况:
from Crypto.Util.number import getPrime,isPrime
def nextPrime(p):
p=(p+2)|1 #一般p是一个素数
while not isPrime(p):
p+=2
return p
def genKey(bits):
p=getPrime(bits)
q=nextPrime(p)
e=65537
n=p*q
return n
这样的话一般来说生成的两个数p和q相差仅仅不过几百或者几千
破解原理:
此时,会有如下关系:
p 2 < n < q 2 p^2 < n <q^2 p2<n<q2
如果对n开近似平方根:
p < √ n < q p<√n<q p<√n<q
则近似平方根必然落在p和q之间,并且距离p和q很近,可以通过穷举的方式找到p和q。
4.(模不互素)
当两个模数共有一个素数时,有如下关系:
n1=p*q1
n2=p*q2
可以对n1和n2求最大公约数(gcd),这两者的最大公约数即为其中的一个素因子p,从而可以分解为这两个模数。
#案例代码:
p=getPrime(1024)
q1=getPrime(1024)
q2=getPrime(1024)
n1=p * q1
n2=p * q2
#解题代码
from Crypto.Util.number import GCD
p=GCD(n1,n2)
q1=n1 // p #整除
q2=n2 // p
5.共模攻击:
两个用户使用相同的模数,不同的私钥,加密同一明文消息时存在“共模”
#案例代码
p = getPrime(512)
q = getPrime(512)
n = p*q
e1 = getPrime(64)
e2 = getPrime(64)
m = bytes_to_long(flag)
c1 = pow(m,e1,n)
c2 = pow(m,e2,n)
RSA解密可以看作是,对C开e次方根;或者说是,找到一个d,使得
( m e ) d ≡ m 1 ( m o d n ) (m^e)^d≡m^1(mod n) (me)d≡m1(modn)
目的是为了让m右上角的指数变成1。
这在只有一个 c ≡ m e ( m o d n ) c≡m^e(mod n ) c≡me(modn)时是很难得的,因此也被称为RSA Problem
目前已经有了两组这样的关系:
m^e1≡c1(mod n )
m^e2≡c2(mod n )
可以通过扩展欧几里得算法计算出
re1 + se2 = 1
从而有:
C1^r * C2^s ≡ m^(re1 + se2)≡m^1(mod n )
使得右上角的指数变成1
def egcd(a, b):
'''
Extended Euclidean Algorithm.
returns x, y, gcd(a,b) such that ax + by = gcd(a,b).
'''
u, u1 = 1, 0
v, v1 = 0, 1
while b:
q, r = divmod(a, b)
u, u1 = u1, u - q * u1
v, v1 = v1, v - q * v1
a, b = b, r
return u, v, a
r, s, _ = egcd(e1, e2)
if r < 0:
r = -r
c1 = inverse(c1, n)
else:
s = -s
c2 = inverse(c2, n)
m = pow(c1, r, n) * pow(c2, s, n) % n
print(long_to_bytes(m))
RSA指数相关利用手法:
1.小公钥指数的利用:
1.引言:
RSA算法通过模幂运算对明文信息加密,当指数逐渐增大时,模运算将会发挥作用,将整数的幂运算的结果截断到有限的范围内。
但是当指数太小时,模运算还未发挥作用,幂运算就已经结束了。此时的加密结果并没有被解截断,即是原本的幂运算,此时就不存在“加密”效果了。
C ≡ m e ( m o d n ) = = > c = m e C≡m^e (mod n ) ==> c =m^e C≡me(modn)==>c=me
2.什么情况下会发生小公钥指数利用?
当m较小时,即m^e < n时,就会存在这种利用。
另外,即使m^e稍比n大一点点,也可以通过穷举的方式对其尝试开根。
c ≡ m e ( m o d n ) ⇒ c = m e − k ⋅ n c \equiv m^e \pmod{n} \Rightarrow c = m^e - k \cdot n c≡me(modn)⇒c=me−k⋅n
可以从0开始穷举k,并对k·n+c尝试开e次方根,若可以开出来根,则说明成功解密。(对于正常的RSA加密,k一般很大,无法被穷举)
3.代码:
from sympy import nth_root
# 假设这些值是已知的
c = 123456789 # 密文
e = 3 # 公钥指数
n = 323 # 模数
# 尝试穷举k来破解RSA加密
for k in range(0, 1000000):
tmp = k * n + c
root = nth_root(tmp, e, 1)[0] # 计算e次方根
if root.is_integer and root > 0: # 检查根是否为正整数
print(f"成功解密: 明文m = {
int(root)}")
break
else:
print("在给定的范围内未能成功解密。")
2.已知e和d分解n
RSA算法若能够知道加密指数e和解密指数d,则可以完成对n的分解。
根据e和d的关系有
e ⋅ d ≡ 1 ( mod ϕ ( n ) ) e \cdot d \equiv 1 (\text{mod} \phi(n)) e⋅d≡1(modϕ(n))
同样可以化为
⇒ e ⋅ d = 1 + k ⋅ ϕ ( n ) \Rightarrow e \cdot d = 1 + k \cdot \phi(n) ⇒e⋅d=1+k