[Crypto学习笔记] 简单的RSA类题目
发布时间:
有一段时间我在进行反思, 受以前的一些经历限制, 一直以来是跳跃性地学习密码学, 以至于一边在忙着一些复杂的东西, 但又有很多基本概念实际上并没有搞懂, 所以后来有了些想法, 决定比较系统地梳理一下知识点.
目录
前置概念
rsa原理
从最基本的rsa开始, 标准题目中可以分解n为p, q构造phin获取私钥:
gcd
即求取多个数的最大公因数, 考虑到数可以由一个或多个素数相乘构成, 在多个数中共同构成的素数乘积即为最大公因数, 基本数学概念, 以下举个简单例子:
from math import gcd
a = 7 * 11 * 3 * 2
b = 7 * 11 * 5
c = 7 * 11
print(gcd(a,b) == c)
# True
可以通过辗转相除法获取最大公因数.
hgcd
全称Half-GCD, 在多项式中取gcd的一种算法, 多项式同样可以分解为一个或多个多项式, 相比较直接gcd, hgcd在取模运算上做了优化, 想象有项数极高的两个多项式, 在最坏情况下每次辗转相除可以消去一项, 实际上在整除过程中往往只需要在高次项就可以计算出答案, 但是取模需要精度计算到最后, 造成了计算资源浪费. 考虑存在以下两个多项式1:
$\color{#FF0000}但是我真的写不动了, 所以决定以后再说.$
\[\begin{matrix} f(x) \\ g(x) \end{matrix}\]以下偷取一段Half-GCD的代码进行分析:
def HGCD(a, b):
if 2 * b.degree() <= a.degree() or a.degree() == 1:
return 1, 0, 0, 1
m = a.degree() // 2
a_top, a_bot = a.quo_rem(x^m)
b_top, b_bot = b.quo_rem(x^m)
R00, R01, R10, R11 = HGCD(a_top, b_top)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
q, e = c.quo_rem(d)
d_top, d_bot = d.quo_rem(x^(m // 2))
e_top, e_bot = e.quo_rem(x^(m // 2))
S00, S01, S10, S11 = HGCD(d_top, e_top)
RET00 = S01 * R00 + (S00 - q * S01) * R10
RET01 = S01 * R01 + (S00 - q * S01) * R11
RET10 = S11 * R00 + (S10 - q * S11) * R10
RET11 = S11 * R01 + (S10 - q * S11) * R11
return RET00, RET01, RET10, RET11
def GCD(a, b):
print(a.degree(), b.degree())
q, r = a.quo_rem(b)
if r == 0:
return b
R00, R01, R10, R11 = HGCD(a, b)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
if d == 0:
return c.monic()
q, r = c.quo_rem(d)
if r == 0:
return d
return GCD(d, r)
相关信息攻击
相关信息类题型一般涉及franklin-reiter攻击和HalfGCD算法考点, 以下先定义相关信息的题型:
\[\begin{matrix} c_1\equiv m_1^e \mod{n} \\ c_2\equiv m_2^e \mod{n} \\ m_2 = am_1+b \end{matrix}\]考虑到
\[m_2 \equiv f(m_1) \mod{n}\]那么我们可以知道m2是
\(f(x)^e \equiv c_1 \mod{n}\) 的一个解,即它是方程 \(f(x)^e-c_1\) 在模n意义下的一个根.同样,m2是 \(x^e\) 模n意义下的一个根.所以有 \(x-m_2\) 同时整除以上两个多项式, 可以求得两个多项式的最大公因子, 如果最大公因子恰好是线性的话, 那么我们可以求得m2. 在e=3的情况下, 最大公因子一定是线性的.
当e=3,且
\[f(x)=ax+b\]的情况下,经过推导我们可以得到:
上面的式子中右边所有的参数都是已知的,所以可以求得对应的信息.
glacierctf2024
Rivest_Shamir_Adleman_Germain
题目
很基础的计算题, 不知道为什么三期竟然没有密码手秒一下, 等报告的过程中顺便写了一下:
#!/usr/bin/env sagemath
# -*- coding: utf-8 -*-
# Respect the shebang and mark file as executable
import os
from Crypto.Util.number import getPrime
from Crypto.Util.number import isPrime
from Crypto.Util.number import bytes_to_long
def generate_primes():
while True:
p = getPrime(512),
q = (2*p) + 1
r = (2*q) + 1
s = (2*r) + 1
if isPrime(q) and isPrime(r) and isPrime(s):
break
return (p, q, r, s)
def main() -> int:
(p, q, r, s) = generate_primes()
N = p * q * r * s
e = 0x10001
with open("flag.txt", "r") as flag_file:
flag = flag_file.read().strip()
CT = pow(bytes_to_long(flag.encode()), e, N)
print(f"{N = }")
print(f"{CT = }")
return 0
if __name__ == '__main__':
raise SystemExit(main())
# vim:filetype=python
N = 489654925303072532553659432557377999607856370197579144782976005904927235244321459117898721690940319487769632950077647476152880207627385231603017537961906244964117707813500615680799967895028255666319186794462243666159201392490299439947399915406223652423977002396844720487588735149486903743362109592536081726574342051928022071576485169655694281378301551060632699138055044915993078059902577590451519251321215765308977494770310317350866241246677761542212605478044672014913289740381478940929584556588858045439572693806615268502627912952686133840081188641597461343817750411035667135310831687533531094008308185320371643348451
CT = 58535947031303233853656030097871859886777764034955095086618901763996192727846608049414977429851683454541344096765319691912454768331685486037922533236779909508856486986528041125267338846421238077083738092020495236946742989769815669001100361743526446503248639704900983287986142636083524250650662602975802778548032518674346903013262799229594298599457623347987250272218522320743415393958131916181915804368140008312975210397791293701839101635851486434802271100141743496402698229558250485987421664294229816166263965806962242894230766553316312608696594536239328785792283453559549564751529321240567418095487324718881437825650
解析
题目很清楚有如下关系:
\[\begin{matrix} N = p*q*r*s \\ q = (2*p) + 1 \\ r = (2*q) + 1 \\ s = (2*r) + 1 \end{matrix}\]因为不喜欢读题结果又考虑复杂了, 既然给出了p,q,r,s几个素数的关系, 可以考虑设未知数去求小根, 结果没解出来, 疑惑了好久又重新一点点审计代码, 发现竟然没有取模, 所以本质只是个简单列方程求未知数, 可以参考sage文档构造EXP.
EXP
使用了EXP库, 或者直接调用sagemath和Crypto库也可以.
from EXP import *
N = 489654925303072532553659432557377999607856370197579144782976005904927235244321459117898721690940319487769632950077647476152880207627385231603017537961906244964117707813500615680799967895028255666319186794462243666159201392490299439947399915406223652423977002396844720487588735149486903743362109592536081726574342051928022071576485169655694281378301551060632699138055044915993078059902577590451519251321215765308977494770310317350866241246677761542212605478044672014913289740381478940929584556588858045439572693806615268502627912952686133840081188641597461343817750411035667135310831687533531094008308185320371643348451
CT = 58535947031303233853656030097871859886777764034955095086618901763996192727846608049414977429851683454541344096765319691912454768331685486037922533236779909508856486986528041125267338846421238077083738092020495236946742989769815669001100361743526446503248639704900983287986142636083524250650662602975802778548032518674346903013262799229594298599457623347987250272218522320743415393958131916181915804368140008312975210397791293701839101635851486434802271100141743496402698229558250485987421664294229816166263965806962242894230766553316312608696594536239328785792283453559549564751529321240567418095487324718881437825650
e = 0x10001
p = var('p')
q = (2*p) + 1
r = (2*q) + 1
s = (2*r) + 1
func_N = p*q*r*s - N
#print(solve(func_N, p))
p = 9352496155192295944243473644483853835662636576410969996619180877861158926367873785037099054018741236476166923118647057249968914650337399039210616026612969
q = (2*p) + 1
r = (2*q) + 1
s = (2*r) + 1
phin = (p-1)*(q-1)*(r-1)*(s-1)
d = pow(e,-1,phin)
print(l2b(pow(CT, d, N)))
sictfround3
gggcccddd
题目
from Crypto.Util.number import *
from enc import flag
m = bytes_to_long(flag)
p = getPrime(512)
q = getPrime(512)
n = p*q
e = 65537
c1 = pow(m,e,n)
c2 = pow(233*m+9527,e,n)
print(f'n = {n}')
print(f'c1 = {c1}')
print(f'c2 = {c2}')
print(f'e = {e}')
"""
n = 71451784354488078832557440841067139887532820867160946146462765529262021756492415597759437645000198746438846066445835108438656317936511838198860210224738728502558420706947533544863428802654736970469313030584334133519644746498781461927762736769115933249195917207059297145965502955615599481575507738939188415191
c1 = 60237305053182363686066000860755970543119549460585763366760183023969060529797821398451174145816154329258405143693872729068255155086734217883658806494371105889752598709446068159151166250635558774937924668506271624373871952982906459509904548833567117402267826477728367928385137857800256270428537882088110496684
c2 = 20563562448902136824882636468952895180253983449339226954738399163341332272571882209784996486250189912121870946577915881638415484043534161071782387358993712918678787398065688999810734189213904693514519594955522460151769479515323049821940285408228055771349670919587560952548876796252634104926367078177733076253
e = 65537
"""
分析
最开始认识franklin板子的题目, 当时只是理解了相关信息攻击, 正式学习HGCD还是又花了一段时间. 题目很标准, 不过e相对来说有点太大了, 如果只是用flanklin的板子许需要跑很久, 因此需要套用HGCD降低复杂度.
EXP
from sage.all import *
from Crypto.Util.number import *
import sys
def HGCD(a, b):
if 2 * b.degree() <= a.degree() or a.degree() == 1:
return 1, 0, 0, 1
m = a.degree() // 2
a_top, a_bot = a.quo_rem(x**m)
b_top, b_bot = b.quo_rem(x**m)
R00, R01, R10, R11 = HGCD(a_top, b_top)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
q, e = c.quo_rem(d)
d_top, d_bot = d.quo_rem(x**(m // 2))
e_top, e_bot = e.quo_rem(x**(m // 2))
S00, S01, S10, S11 = HGCD(d_top, e_top)
RET00 = S01 * R00 + (S00 - q * S01) * R10
RET01 = S01 * R01 + (S00 - q * S01) * R11
RET10 = S11 * R00 + (S10 - q * S11) * R10
RET11 = S11 * R01 + (S10 - q * S11) * R11
return RET00, RET01, RET10, RET11
def GCD(a, b):
q, r = a.quo_rem(b)
if r == 0:
return b
R00, R01, R10, R11 = HGCD(a, b)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
if d == 0:
return c.monic()
q, r = c.quo_rem(d)
if r == 0:
return d
return GCD(d, r)
sys.setrecursionlimit(500000)
n = 71451784354488078832557440841067139887532820867160946146462765529262021756492415597759437645000198746438846066445835108438656317936511838198860210224738728502558420706947533544863428802654736970469313030584334133519644746498781461927762736769115933249195917207059297145965502955615599481575507738939188415191
c1 = 60237305053182363686066000860755970543119549460585763366760183023969060529797821398451174145816154329258405143693872729068255155086734217883658806494371105889752598709446068159151166250635558774937924668506271624373871952982906459509904548833567117402267826477728367928385137857800256270428537882088110496684
c2 = 20563562448902136824882636468952895180253983449339226954738399163341332272571882209784996486250189912121870946577915881638415484043534161071782387358993712918678787398065688999810734189213904693514519594955522460151769479515323049821940285408228055771349670919587560952548876796252634104926367078177733076253
e = 65537
R = PolynomialRing(Zmod(n), 'x')
x = R.gen()
f = x**e - c1
g = (233*x+9527)**e - c2
res = GCD(f,g)
m = -res.monic().coefficients()[0]
flag = long_to_bytes(int(m))
print(flag)
# SICTF{45115fb2-84d6-4369-88c2-c8c3d72b4c55}
secconctf
reiwa_rot13
题目
挺开心的一个题, 在贵大和密码师傅们现场写了一下, 结果少条件了没出, 当场社死()
from Crypto.Util.number import *
import codecs
import string
import random
import hashlib
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from flag import flag
p = getStrongPrime(512)
q = getStrongPrime(512)
n = p*q
e = 137
key = ''.join(random.sample(string.ascii_lowercase, 10))
rot13_key = codecs.encode(key, 'rot13')
key = key.encode()
rot13_key = rot13_key.encode()
print("n =", n)
print("e =", e)
print("c1 =", pow(bytes_to_long(key), e, n))
print("c2 =", pow(bytes_to_long(rot13_key), e, n))
key = hashlib.sha256(key).digest()
cipher = AES.new(key, AES.MODE_ECB)
print("encyprted_flag = ", cipher.encrypt(flag))
解析
很基本的类型, 一眼相关信息攻击魔改, 不过在构造线性关系中采用了rot13, 其实原题很简单, 因为key被限制为10位字母: ''.join(random.sample(string.ascii_lowercase, 10))
, 我们可以直接得知关系式:
其中b为10位中对256倍数(在long_to_bytes中是256进制, 奇妙的知识增加了)加或减13的和, 也就是有2**10
种可能. 当时只顾着flanklin秒了, 前面的key没读题, 当成参有数字随机的不定长度情况去做, 最后只找到b是13的倍数, 还以为需要改板子.
因为多项式足够小, 根据原理可知hgcd甚至会拖慢速度, 所以直接构造秒了.
EXP
from Crypto.Util.number import *
from sage.all import *
from itertools import product
from Crypto.Cipher import AES
import hashlib
def compositeModulusGCD(a, b):
if (b == 0):
return a.monic()
return compositeModulusGCD(b, a % b)
def franklinReiter(n, e, c1, c2, b):
R = Zmod(n)['x']
x = R.gen()
f = x**e - c1
g = (x+b) ** e - c2
m = int(n-(compositeModulusGCD(f,g)).coefficients()[0])
return m
n = 105270965659728963158005445847489568338624133794432049687688451306125971661031124713900002127418051522303660944175125387034394970179832138699578691141567745433869339567075081508781037210053642143165403433797282755555668756795483577896703080883972479419729546081868838801222887486792028810888791562604036658927
e = 137
c1 = 16725879353360743225730316963034204726319861040005120594887234855326369831320755783193769090051590949825166249781272646922803585636193915974651774390260491016720214140633640783231543045598365485211028668510203305809438787364463227009966174262553328694926283315238194084123468757122106412580182773221207234679
c2 = 54707765286024193032187360617061494734604811486186903189763791054142827180860557148652470696909890077875431762633703093692649645204708548602818564932535214931099060428833400560189627416590019522535730804324469881327808667775412214400027813470331712844449900828912439270590227229668374597433444897899112329233
encyprted_flag = b"\xdb'\x0bL\x0f\xca\x16\xf5\x17>\xad\xfc\xe2\x10$(DVsDS~\xd3v\xe2\x86T\xb1{xL\xe53s\x90\x14\xfd\xe7\xdb\xddf\x1fx\xa3\xfc3\xcb\xb5~\x01\x9c\x91w\xa6\x03\x80&\xdb\x19xu\xedh\xe4"
count = 0
for i in product([13, -13], repeat=10):
b = 0
for j in range(len(i)):
b += i[j] * (0x100**j)
m = franklinReiter(n,e,c1,c2,b)
key = long_to_bytes(m)
try:
print(key.decode())
key = hashlib.sha256(key).digest()
cipher = AES.new(key, AES.MODE_ECB)
flag = cipher.decrypt(encyprted_flag)
print(flag)
break
except:
count += 1
print(f'{count}/1024')
'''
......
792/1024
793/1024
794/1024
dnjqygbmor
b'SECCON{Vim_has_a_command_to_do_rot13._g?_is_possible_to_do_so!!}'
'''
参考文献
whx1003.多项式gcd的正确姿势:Half-GCD算法[EB/OL]博客园.https://www.cnblogs.com/whx1003/p/16217087.html.2022-05-02. ↩