hamburgerRSA
题目描述
from Crypto.Util.number import *
flag = open('flag.txt').read()
nbit = 64
while True:
p, q = getPrime(nbit), getPrime(nbit)
PP = int(str(p) + str(p) + str(q) + str(q))
QQ = int(str(q) + str(q) + str(p) + str(p))
if isPrime(PP) and isPrime(QQ):
break
n = PP * QQ
m = bytes_to_long(flag.encode())
c = pow(m, 65537, n)
print('n =', n)
print('c =', c)
程序分析
p,q是随机选取64位二进制的素数;
关键是生成的公私钥使用的是 P P , Q Q PP,QQ PP,QQ;
而 P P , Q Q PP,QQ PP,QQ是通过简单的字符串 p , q p,q p,q连接的形式组成
本题的关键就是如何去表示 P P , Q Q PP,QQ PP,QQ
由于是采用字符串相连接的形式,我们可以使用 1 0 x ∗ p + 1 0 y ∗ q 10^x*p+10^y*q 10x∗p+10y∗q这样的形式来表示 P P , Q Q PP,QQ PP,QQ
那么
设 x = l e n ( s t r ( p ) ) x = len(str(p)) x=len(str(p)); y = l e n ( s t r ( q ) ) y = len(str(q)) y=len(str(q))
P P = 1 0 x + 2 ∗ y ∗ p + 1 0 2 ∗ y ∗ p + 1 0 y ∗ q + q PP = 10^{x+2*y}*p+10^{2*y}*p+10^{y}*q+q PP=10x+2∗y∗p+102∗y∗p+10y∗q+q
Q Q = 1 0 2 ∗ x + y ∗ q + 1 0 2 ∗ x ∗ q + 1 0 x ∗ p + p QQ=10^{2*x+y}*q+10^{2*x}*q+10^x*p+p QQ=102∗x+y∗q+102∗x∗q+10x∗p+p
两者相乘表示为 n n n
n = P P ∗ Q Q = 1 0 3 ∗ x + 3 ∗ y ∗ p ∗ q + 1 0 3 ∗ x + 2 ∗ y ∗ p ∗ q + 1 0 2 ∗ x + 3 ∗ y ∗ p ∗ q + . . . + 1 0 x ∗ p ∗ q + 1 0 x ∗ p ∗ q + p ∗ q n = PP*QQ=10^{3*x+3*y}*p*q+10^{3*x+2*y}*p*q+10^{2*x+3*y}*p*q+...+10^{x}*p*q+10^{x}*p*q+p*q n=PP∗QQ=103∗x+3∗y∗p∗q+103∗x+2∗y∗p∗q+102∗x+3∗y∗p∗q+...+10x∗p∗q+10x∗p∗q+p∗q
中间的省略号部分是因为与之后的推导没有关系(也可以说是由于加法前后两项重合了数位无法简单表示),所以省略不考虑了
通过观察,已知的 n n n实际上是由 p ∗ q p*q p∗q的10的 k k k次方组成的
到这里我们发现n的某一部分数位其实就是 p ∗ q p*q p∗q的数位(比如 15600000 15600000 15600000,它的数位是6-8),但是由于加法前后两项之间有重合,解释一下
比如 1 0 3 ∗ x + 3 ∗ y ∗ p ∗ q 10^{3*x+3*y}*p*q 103∗x+3∗y∗p∗q和 1 0 3 ∗ x + 2 ∗ y ∗ p ∗ q 10^{3*x+2*y}*p*q 103∗x+2∗y∗p∗q,假设 p ∗ q p*q p∗q的最高位为 40 40 40, x = 19 , y = 20 x=19,y=20 x=19,y=20
那么前者的数位覆盖了117-157;后者的数位覆盖了97-137
这里就有重叠的地方了,由于重叠,加法运算之后,我们就没有办法直接用 n n n的这一部分直接等于 p ∗ q p*q p∗q的某一部分了
那么为了进一步表示 n n n,我们需要知道 x , y x,y x,y的确切大小
x , y x,y x,y的确切大小
通过 n n n的表达式,我们可以知道 n n n的最高位(10进制)等于 1 0 3 ∗ x + 3 ∗ y ∗ p ∗ q 10^{3*x+3*y}*p*q 103∗x+3∗y∗p∗q的位数(10进制)
这里我做了一下测试:
while True:
p = getPrime(64)
q = getPrime(64)
temp = p*q
print(len(str(p)),end=" ")
print(len(str(q)),end=" ")
print(len(str(temp)))
可以观察到 p , q p,q p,q的十进制位数是 19 19 19或者 20 20 20,也就是说 x = 19 , 20 x=19,20 x=19,20; y = 19 , 20 y=19,20 y=19,20
而 p ∗ q p*q p∗q的十进制位数为 39 39 39或者 38 38 38
n n n的十进制位数为 156 156 156
剩下的就是一个加法运算,如果 x = y = 20 x=y=20 x=y=20
那么 3 ∗ x + 3 ∗ y + l e n ( p ∗ q ) 3*x+3*y+len(p*q) 3∗x+3∗y+len(p∗q)至少等于 159 159 159;
显然不满足条件;
如果 x = y = 19 x=y=19 x=y=19;
那么 3 ∗ x + 3 ∗ y + l e n ( p ∗ q ) 3*x+3*y+len(p*q) 3∗x+3∗y+len(p∗q)最多等于 153 153 153
也不满足条件
如果 x = 19 , y = 20 x=19,y=20 x=19,y=20(这里 x , y x,y x,y的大小可以互调的)
那么 3 ∗ x + 3 ∗ y + l e n ( p ∗ q ) 3*x+3*y+len(p*q) 3∗x+3∗y+len(p∗q)有等于 156 156 156的可能性
综上, x = 19 , y = 20 x=19,y=20 x=19,y=20;另外也可以知道 p ∗ q p*q p∗q的十进制数位为 39 39 39
p ∗ q p*q p∗q的确切大小
根据之前重叠的说法,我们只需要看在 n n n中没有重叠的部分,没有重叠的部分是直接等于对应的 p ∗ q p*q p∗q的对应数位的;
n n n的高位:
与 1 0 3 ∗ x + 3 ∗ y ∗ p ∗ q 10^{3*x+3*y}*p*q 103∗x+3∗y∗p∗q, 1 0 3 ∗ x + 2 ∗ y ∗ p ∗ q 10^{3*x+2*y}*p*q 103∗x+2∗y∗p∗q, 1 0 2 ∗ x + 3 ∗ y ∗ p ∗ q 10^{2*x+3*y}*p*q 102∗x+3∗y∗p∗q有关
数位分别覆盖117-156;97-136;98-137
那么没有重叠的部分只有前19位的高位(实际上我认为还需要考虑加法之后的进位问题,也就是说可能只有18位的高位与 p ∗ q p*q p∗q相同,但是幸运的是在这道题关键位数没有进位,之后就可以少爆破两位)
n n n的低位:
与 1 0 x ∗ p ∗ q 10^{x}*p*q 10x∗p∗q, 1 0 y ∗ p ∗ q 10^{y}*p*q 10y∗p∗q, p ∗ q p*q p∗q有关
数位分别覆盖19-58;20-59;0-39
没有覆盖的部分只有后18位的低位
总结起来:
已知 p ∗ q p*q p∗q的高19位和低18位
而 p ∗ q p*q p∗q的总位数为 39 39 39
剩下两位需要爆破一下
由于 p ∗ q p*q p∗q不大,可以直接用sagemath的 f a c t o r ( ) factor() factor()分解,判断条件是
if len(factor(pq)) == 2 and factor(pq)[0][0].nbits() == 64:
得到 p p p和 q q q之后计算得到 P P , Q Q PP,QQ PP,QQ就进行正常的RSA求密
代码实现
# sage
nbit = 64
n = 177269125756508652546242326065138402971542751112423326033880862868822164234452280738170245589798474033047460920552550018968571267978283756742722231922451193
high = str(n)[:19]
low = str(n)[-18:]
for i in range(10):
for j in range(10):
pq = int(high + str(i) + str(j) + low)
f = factor(pq)
if len(f) == 2 and f[0][0].nbits() == 64:
p = f[0][0]
q = f[1][0]
print(p,q)
from Crypto.Util.number import *
import gmpy2
p,q = 9788542938580474429,18109858317913867117
c = 47718022601324543399078395957095083753201631332808949406927091589044837556469300807728484035581447960954603540348152501053100067139486887367207461593404096
e = 65537
PP = int(str(p) + str(p) + str(q) + str(q))
QQ = int(str(q) + str(q) + str(p) + str(p))
fai_n = (PP-1)*(QQ-1)
d = gmpy2.invert(e,fai_n)
m = pow(c,d,PP*QQ)
print(long_to_bytes(m))
参考文章(wp)
第四届美团网络安全高校挑战赛Write up (qq.com)
然后这道是类似的题目: