RSA学习笔记

个人的RSA学习笔记

原由

RSA是CTF密码学中非常常考的题目,前一阵配置服务器密钥登录的时候,也涉及到了公钥私钥,于是研究了一下

理论

RSA涉及到了一些数学概念性的东西

1.因数:其实就是小学学的乘数

2.质数:除了1乘自己,没有其它两个整数相乘能得到的数

3.余数:除到个位余下的数,小于除数大于零

4.欧拉函数:我也不知道是什么无伤大雅,名字挺高级挺唬人的

参数

p,q是两个质数

N是两个质数的积(n=p*q)

T是欧拉函数(p-1)*(q-1)

E是公钥 公钥是质数,并且公钥在1和T之间, 正常默认:e=65537

D是私钥 (D*E)%T=1

公式

加密: c=m^E%N

将明文的公钥次方模N得到密文

解密: m=c^D%N

将密文的私钥次方模N得到明文

脚本实现

pow()函数用法

RSA求私钥(求逆元)

pow(E,-1,T)

(D*E)%T=1,求D

RSA加解密

解密:c^D%N

pow(c,D,N)

加密:m^E%N

pow(m,E,N)

解密脚本

p=
q=
e=
c=
N=p*q
T=(p-1)*(q-1)
D=pow(E,-1,T)
m=pow(c,D,N)
print(m)

gmpy2库

RSA涉及的数一般比较大,常规算法会出现精度的偏差,推荐使用gmpy2库

安装

pip install gmpy2
# 如果官方源下载失败或者下载慢,可使用其它源
pip install gmpy2 -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com

常用参数

mpz

作用:把超大整数转成 gmpy2 安全类型

用法:

N = gmpy2.mpz(N)
c = gmpy2.mpz(c)

isqrt

作用:整数开平方,分解 N=p*q 专用

S = gmpy2.isqrt( (p-q)*(p-q) + 4*N )
p = ((p+q) + (p-q)) // 2
q = ((p+q) - (p-q) // 2

gcd

作用:求两个数最大公约数个 n1,n2 直接 gcd(n1,n2) 出 p

invert

作用:求模逆元, 求私钥 d 专用

d=e^−1mod T

d = gmpy2.invert(e, T)

powmod(a, b, N)

作用:加密、解密都靠它

m = c^d mod N

c = m^e mod N

# 解密
m = gmpy2.powmod(c, d, N)
# 加密
c = gmpy2.powmod(m, e, N)

常见题型

经典常规型

给出p,q,e,c,求m

解密脚本

p=
q=
e=
c=
N=p*q
T=(p-1)*(q-1)
D=pow(E,-1,T)
m=pow(c,D,N)
print(m)

单模多密文

给出n,e list_c,求m

from Crypto.Util.number import long_to_bytes
n=
e=65537
list_c=[]
# 分解p,q
 def get_pq(n):
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0:
            return (i, n // i)
# 如果不确定是否为质数,需要return (n,1)
#    return (n, 1) 
p, q = get_pq(n)
T=(p-1)*(q-1)
d=pow(e,-1,T)
flag=b''
for c in list_c:
    m=pow(c,d,n)
    flag+=long_to_bytes(m)
print(flag)

共模攻击

同一套 n,两个不同 e,加密同一个 m → 不用分解 n 就能直接算出 m

思路:同 n + 同 m + 互质 e → 直接算 m,不拆 n、不求 d

n = 
e1 = 
e2 = 
c1 = 
old_r, r = e1, e2
old_s, s = 1, 0
old_t, t = 0, 1
while r != 0:
    q = old_r // r
    old_r, r = r, old_r - q * r
    old_s, s = s, old_s - q * s
    old_t, t = t, old_t - q * t
x = old_s
y = old_t
# 共模解密 m = c1^x * c2^y mod n
m = (pow(c1, x, n) * pow(c2, y, n)) % n
print("明文 m =", m)
# 转字符串
print("明文字符串 =", bytes.fromhex(hex(m)[2:]).decode())

低指数广播攻击

import gmpy2
from Crypto.Util.number import long_to_bytes
n1 = 
c1 = 
n2 = 
c2 = 
n3 = 
c3 = 
def crt(a_list, m_list):
    M = 1
    for m in m_list:
        M *= m
    result = 0
    for a, m in zip(a_list, m_list):
        Mi = M // m
        inv = gmpy2.invert(Mi, m)
        result += a * Mi * inv
    return result % M
# 合并得到 m^3
x = crt([c1, c2, c3], [n1, n2, n3])

# 开立方根
m, _ = gmpy2.iroot(x, 3)
print(m)
print("临时密码 (十六进制):", hex(m))
print("临时密码 (字符串):", long_to_bytes(m))

题目给文件

不管文件是什么,一律用二进制模式读取,最安全、最通用、不会出错!

万能、通用、不管给什么文件都能用的 Python 读取方法,CTF 里 100% 能用,不用管文件后缀是 .pem/.en/.bin/.txt /.zip全都通吃!

读取任意文件内容(最通用)

# 打开文件,读取所有内容(二进制模式,万能)
with open("你的文件名", "rb") as f:
    data = f.read()

# 查看内容
print("文件原始字节内容:", data)

如果内容是文本(公钥.pem 这种)

with open("公钥.pem", "r") as f:
    text = f.read()

print(text)

如果是密文,可能是 base64 编码(CTF 超常见)

很多 .en 密文是 base64 编码,需要先解码:

import base64

with open("密文.en", "rb") as f:
    cipher_b64 = f.read()

cipher_bytes = base64.b64decode(cipher_b64)
print("解码后的密文字节:", cipher_bytes)

把读取到的内容转成大整数(RSA 解密必须)

RSA 运算需要把文件内容变成数字,这是 CTF 固定操作:

# 字节 → 大整数(RSA 专用)
m = int.from_bytes(data, byteorder="big")
print("大整数:", m)

4 行万能代码

任何文件,直接用这 4 行读取内容,永远不会错

# 万能读取
with open("文件名", "rb") as f:
    content = f.read()

# 转成 RSA 需要的大整数
number = int.from_bytes(content, "big")

CTF 最常见的两种文件怎么读?

读公钥文件(xxx.pem)

from Crypto.PublicKey import RSA

with open("public.pem", "rb") as f:
    key = RSA.import_key(f.read())

n = key.n
e = key.e
print("n =", n)
print("e =", e)

读密文文件(xxx.en)

with open("enc.en", "rb") as f:
    c_bytes = f.read()

c = int.from_bytes(c_bytes, "big")
print("密文数字 c =", c)

一句话总结

CTF 里不管给什么文件:

  1. 先用 rb 模式读成字节
  2. 是文本就直接打印
  3. 是 base64 就解码
  4. 是 RSA 密文就转成大整数

这一套流程通杀所有文件


总结

  • 万能读取open("文件", "rb").read()
  • 文本文件:用 "r" 模式
  • base64 密文base64.b64decode()
  • RSA 运算int.from_bytes(内容, "big")

RSA类型的题目

文章评论