CTF--RSA--p-1光滑
RSA--p-1光滑
对于CTF--Crypto--RSA中的p-1光滑问题,在此记录本人的学习记录以及心得。欢迎各位师傅斧正。
前备知识:
光滑数(Smooth number):可以分解成小素数的正整数。
费马小定理:
\[\begin{flalign}
&(a,p)=1\Longrightarrow a^{p-1}≡1(mod\quad p)\\
&费马小定理证明:\\
&首先你要知道剩余类的概念,不知道的师傅可以去补一下。\\
&对于素数p,它的剩余类是\{0,1,2,\cdots,p-1\},不再讨论0的情况。那么对于\{a,2a,\cdots,(p-1)a\}.\\
&现证明\{a,2a,\cdots,(p-1)a\}中没有相同的元素。就可以说明\{0,a,2a,\cdots,(p-1)a\}也是p的剩余类。\\
&假设有a_i,a_j元素,a_i=a_j(1≤i,j≤p-1)\rightarrow a(i-j)≡0(mod\quad p)\rightarrow p|a(i-j)\\
&这与i,j\in\{0,1,2,\cdots,p-1\}且(a,p)=1矛盾,故\{a,2a,\cdots,(p-1)a\}中没有相同的元素.\\
&则有(p-1)!≡a^{p-1}(p-1)(mod\quad p)且(p,(p-1)!)=1\rightarrow a^{p-1}≡1(mod\quad p)&
\end{flalign}
\]
B-Smooth:
\[\begin{flalign}
&可设p-1 = p_1p_2\cdots p_s(\forall1≤i≤s)\\
&若p_1p_2\cdots p_s两两不同,则p_1p_2\cdots p_s|B!\Longrightarrow B!=k(p - 1).因此a^{B!}≡a^{k(p-1)}≡1(mod p).\\
&假设N=pq,计算gad(a^{B!}-1,N)=p即可.&
\end{flalign}
\]
理论代码:
from Crypto.Util.number import *
from gmpy2 import *
a = 2 # 这个就是费马小定理中的a,我们直接取素数2即可
n = 2 # n用来计算B-Smooth中的B!
while True: # 利用循环找到满足gcd(a^{B!}-1,N) = p的情况
a = pow(a, n, N)
res = GCD(a-1, N)
if res != 1 and res != N:
q = N // res
print("p=",res)
print("q=",q)
break
n += 1
典型例题:
2024SHCTFWeek2--魔鬼的步伐
from Crypto.Util.number import *
from random import choice
from enc import flag
m = bytes_to_long(flag)
def get_primes(limit):
primes = []
is_prime = [True] * (limit + 1)
for num in range(2, limit + 1):
if is_prime[num]:
primes.append(num)
for multiple in range(num * num, limit + 1, num):
is_prime[multiple] = False
return primes
def get_Prime(bits):
while True:
n = 2
while n.bit_length() < bits:
n *= choice(primes)
if isPrime(n + 1):
return n + 1
e = 65537
primes = get_primes(e)
p = get_Prime(512)
q = get_Prime(512)
n = p*q
c = pow(m,e,n)
print(f"n = {n}")
print(f"e = {e}")
print(f"c = {c}")
'''
n = 779579301738188606639541475585479048662338414978423162511086618350581959037809676355175033940672838358216296034989613684765924879020007985463372880378958453235938149726732804842653385407460552866449233717559756619739481517089434393605029261842182116054349584175198599481092380973944251651872382058614325364639417
e = 65537
c = 547032040563518540116157223879027303340872416038133089003193905244978422560735616179910978730578423924420033057963791332760379132346877909806353927165538096867865399527460216074519919572776729647581400118345644864260152466704275743378828075020134070702051989890847762735538767329450466068109132805742740564390842
'''
Analysis:
欧拉函数φ(n): 给定一个正整数 n,欧拉函数就是求在 [1,n) 区间上,与 n互质的整数的个数。举例来说,设m = 8,则与8互质的正整数集合A={1, 3, 5, 7},此集合共有4个元素,所以 φ ( 8 ) = 4。
\[\forall n\in \mathbb{N^+},φ(n)=∣A∣,whereA=\{m|1≤m≤n,(m,n)=1\}\\
|S|表示集合S中的元素个数
\]
欧拉定理: 对任意两个正整数 a , n ,若两者互素,则:对比费马小定理,实际上就是欧拉定理的特殊情况。因为素数p的φ(p)=p-1
\[a^{φ(n)}≡1(mod\ \ n)=>a^{kφ(n)}=(a^{φ(n)})^k≡1^k≡1(mod\ \ n)
\]
所以只要指数是模数n的欧拉函数的倍数,在模n下就等于1.
对于本题中的情况:
\[p = p_1p_2\cdots p_s+1;q=q_1q_2\cdots q_t+1\\
φ(p)=p_1p_2\cdots p_s;φ(q)=q_1q_2\cdots q_t\\
n = pq;φ(n)=p_1p_2\cdots p_sq_1q_2\cdots q_t\\
所以只需要遍历get\_primes()得到的素数表就可以得到数a的计算指数.\\
得,a^{k*φ(n)}≡1(mod\ \ n)\\
a^{k*φ(n)}-1≡0(mod\ \ p)=>gcd(a^{k*φ(n)}-1,n)=p
\]
exp:
from Crypto.Util.number import *
# Given values
n = 779579301738188606639541475585479048662338414978423162511086618350581959037809676355175033940672838358216296034989613684765924879020007985463372880378958453235938149726732804842653385407460552866449233717559756619739481517089434393605029261842182116054349584175198599481092380973944251651872382058614325364639417
e = 65537
c = 547032040563518540116157223879027303340872416038133089003193905244978422560735616179910978730578423924420033057963791332760379132346877909806353927165538096867865399527460216074519919572776729647581400118345644864260152466704275743378828075020134070702051989890847762735538767329450466068109132805742740564390842
# 初始化生成光滑数的列表get_primes(e)
def get_primes(limit):
primes = []
is_prime = [True] * (limit + 1)
for num in range(2, limit + 1):
if is_prime[num]:
primes.append(num)
for multiple in range(num * num, limit + 1, num):
is_prime[multiple] = False
return primes
# 由于n = p * q ,而p,q分别由一个get_primes生成,所以最终n的列表应该是2*get_primes(e)
primes_value = get_primes(e) + get_primes(e)
# 求解φ(n)
def Solve(n):
counter = 0
for p in primes_value:
if n % p == 0:
counter += 1
return n - counter - 2 # 减去1和n的两种情况
# 取素数2当作最初的a,在φ(n)的范围内不断增大指数寻找GCD(a^{k*φ(n)}-1,n) != 0和 != n
a = 2
for _ in range(2,Solve(n)):
a = pow(a,_,n)
p = GCD(a-1,n)
if p !=1 and p != n:
q = n // p
break
phi_n = (p-1)*(q-1)
d = inverse(e,phi_n)
flag = long_to_bytes(pow(c,d,n))
print(flag)
#SHCTF{1rlCTioN_is_ThE_d3vils_st3P_4s}
写在最后:
例题不多,但是我觉得这道题的质量足以锻炼RSA中p-1光滑的题型的考点以及应对策略,我们的最终目的就是寻找到φ(n),之后取a=2利用费马小定理或者欧拉定理进行求解得到a^{φ(n)}≡1(mod p) => gcd(n,a^{φ(n)}-1) = p,判断条件就是gcd(n,a^{φ(n)}-1) !=1 and gcd(n,a^{φ(n)}-1) != n.