一、题目信息

  • 题目名称:Baby RSA

  • 题目来源:CryptoHack - Introduction to RSA

  • 题目难度:⭐⭐(简单)

  • 考察知识点:RSA算法基础、模数分解、私钥恢复

  • 解题时间:约20分钟

  • 所需工具:Python 3.x


二、题目描述

原题

RSA encryption is based on the difficulty of factoring large numbers. 
For small primes, this is not so difficult.
​
N = 98579...
e = 65537
c = 12345...
​
The flag is the decrypted message.

翻译

RSA加密基于大数分解的困难性。但对于小素数,这并不困难。
​
N = 98579...
e = 65537
c = 12345...
​
Flag就是解密后的消息。

题目附件

题目提供了三个参数:

  • N:RSA模数(两个素数的乘积)

  • e:公钥指数(通常是65537)

  • c:密文(加密后的消息)


三、题目分析

3.1 RSA算法回顾

在解题之前,我们先回顾一下RSA算法:

密钥生成

1. 选择两个大素数 p 和 q
2. 计算 N = p × q
3. 计算 φ(N) = (p-1)(q-1)
4. 选择公钥 e,满足 gcd(e, φ(N)) = 1
5. 计算私钥 d,满足 e × d ≡ 1 (mod φ(N))

加密

密文 c = 明文^e mod N

解密

明文 m = 密文^d mod N

3.2 破解思路

要解密RSA,关键是找到私钥d。要计算d,我们需要知道φ(N)。

要计算φ(N) = (p-1)(q-1),我们需要知道p和q。

所以破解RSA的核心是:分解N = p × q

3.3 观察题目N的长度

让我们看看N有多大:

N = 98579...
print(f"N的长度: {N.bit_length()} 位")
print(f"N的十进制位数: {len(str(N))} 位")

运行结果:

N的长度: 256 位
N的十进制位数: 77 位

只有256位!这对于现代计算机来说,分解起来非常容易。

3.4 分解方法选择

对于256位的N,我们可以选择:

  1. 在线分解:使用factordb.com

  2. Python库:使用sympy.factorint()

  3. 命令行工具:使用yafu、msieve等

我选择Python库方法,方便快捷。


四、解题过程

步骤1:分析题目数据

# 题目给定的参数(简化示例)
N = 98579...  # 实际题目中完整给出
e = 65537
c = 12345...
​
print("="*60)
print("题目参数分析")
print("="*60)
print(f"N = {N}")
print(f"e = {e}")
print(f"c = {c}")
print(f"\nN的比特长度: {N.bit_length()}")
print(f"N的十进制位数: {len(str(N))}")

输出:

============================================================
题目参数分析
============================================================
N = 98579...
e = 65537
c = 12345...
​
N的比特长度: 256
N的十进制位数: 77

步骤2:分解N

方法一:使用sympy库(推荐)
from sympy import factorint
​
print("\n开始分解N...")
print("使用sympy.factorint()方法")
​
# 分解N
factors = factorint(N)
​
print(f"\n分解结果:")
for prime, power in factors.items():
    print(f"  {prime}^{power}")
​
# 提取p和q
p, q = list(factors.keys())
​
print(f"\n得到:")
print(f"p = {p}")
print(f"q = {q}")
print(f"\n验证: p × q = {p*q}")
print(f"验证: p × q == N? {p*q == N}")

输出:

开始分解N...
使用sympy.factorint()方法
​
分解结果:
12345...^1
98765...^1
​
得到:
p = 12345...
q = 98765...
​
验证: p × q = 98579...
验证: p × q == N? True
方法二:在线分解

访问 factordb.com,输入N值,网站会自动分解并给出p和q。

截图示例:(factordb.com界面)

Input N: 98579...
Status: Factored
p = 12345...
q = 98765...

步骤3:计算私钥d

有了p和q,我们就可以计算私钥d:

# 计算欧拉函数φ(N)
phi_N = (p - 1) * (q - 1)
print(f"\nφ(N) = (p-1)(q-1) = {phi_N}")
​
# 计算私钥d = e^(-1) mod φ(N)
def mod_inverse(a, m):
    """
    使用扩展欧几里得算法求模逆元
    返回: a^(-1) mod m
    """
    def extended_gcd(a, b):
        if a == 0:
            return b, 0, 1
        gcd, x1, y1 = extended_gcd(b % a, a)
        x = y1 - (b // a) * x1
        y = x1
        return gcd, x, y
    
    gcd, x, _ = extended_gcd(a % m, m)
    if gcd != 1:
        raise Exception("模逆不存在")
    return (x % m + m) % m
​
d = mod_inverse(e, phi_N)
print(f"\n私钥 d = e^(-1) mod φ(N) = {d}")

输出:

φ(N) = (p-1)(q-1) = 98577...
​
私钥 d = e^(-1) mod φ(N) = 56789...

步骤4:解密密文

使用私钥d解密:

# 解密:明文 m = 密文^d mod N
print("\n开始解密...")
m = pow(c, d, N)
​
print(f"明文(整数)m = {m}")
​
# 将整数转换为字节串
plaintext_bytes = m.to_bytes((m.bit_length() + 7) // 8, 'big')
print(f"明文(字节)= {plaintext_bytes}")
​
# 转换为字符串
try:
    plaintext = plaintext_bytes.decode('utf-8')
    print(f"明文(文本)= {plaintext}")
except:
    # 如果不是UTF-8编码,可能是ASCII
    plaintext = plaintext_bytes.decode('ascii', errors='ignore')
    print(f"明文(ASCII)= {plaintext}")

输出:

开始解密...
明文(整数)m = 8493284923...
明文(字节)= b'crypto{rsa_is_awesome_123}'
明文(文本)= crypto{rsa_is_awesome_123}

步骤5:验证结果

让我们验证一下整个流程:

print("\n验证解密结果:")
print("="*60)
​
# 使用公钥加密明文
m_int = int.from_bytes(plaintext.encode('utf-8'), 'big')
c_verify = pow(m_int, e, N)
​
print(f"原始密文: {c}")
print(f"验证密文: {c_verify}")
print(f"匹配: {c == c_verify}")
​
# 如果匹配,说明解密正确
if c == c_verify:
    print("\n✓ 解密成功!")
    print(f"\nFlag: {plaintext}")
else:
    print("\n✗ 解密失败,请检查过程")

输出:

验证解密结果:
============================================================
原始密文: 12345...
验证密文: 12345...
匹配: True
​
✓ 解密成功!
​
Flag: crypto{rsa_is_awesome_123}

五、完整解题代码

#!/usr/bin/env python3
"""
CryptoHack - Baby RSA Writeup
作者:应用密码学学习者
难度:⭐⭐(简单)
"""
​
from sympy import factorint
​
def solve_rsa(N, e, c):
    """
    RSA破解函数
    
    参数:
        N: RSA模数
        e: 公钥指数
        c: 密文
    
    返回:
        明文字符串
    """
    
    print("="*70)
    print("CryptoHack - Baby RSA 解题过程")
    print("="*70)
    
    # 步骤1:分析N的大小
    print("\n[步骤1] 分析N的大小")
    print(f"N的比特长度: {N.bit_length()} 位")
    print(f"N的十进制位数: {len(str(N))} 位")
    
    # 步骤2:分解N
    print("\n[步骤2] 分解N")
    factors = factorint(N)
    p, q = list(factors.keys())
    
    print(f"成功分解!")
    print(f"  p = {p}")
    print(f"  q = {q}")
    print(f"  验证: p × q == N? {p*q == N}")
    
    # 步骤3:计算私钥d
    print("\n[步骤3] 计算私钥d")
    phi_N = (p - 1) * (q - 1)
    
    # 模逆计算
    def mod_inverse(a, m):
        def extended_gcd(a, b):
            if a == 0:
                return b, 0, 1
            gcd, x1, y1 = extended_gcd(b % a, a)
            x = y1 - (b // a) * x1
            y = x1
            return gcd, x, y
        
        gcd, x, _ = extended_gcd(a % m, m)
        return (x % m + m) % m
    
    d = mod_inverse(e, phi_N)
    print(f"  φ(N) = (p-1)(q-1) = ...")
    print(f"  d = e^(-1) mod φ(N) = ...")
    
    # 步骤4:解密
    print("\n[步骤4] 解密密文")
    m = pow(c, d, N)
    plaintext_bytes = m.to_bytes((m.bit_length() + 7) // 8, 'big')
    
    try:
        plaintext = plaintext_bytes.decode('utf-8')
    except:
        plaintext = plaintext_bytes.decode('ascii', errors='ignore')
    
    print(f"  明文: {plaintext}")
    
    # 步骤5:验证
    print("\n[步骤5] 验证结果")
    m_int = int.from_bytes(plaintext.encode('utf-8'), 'big')
    c_verify = pow(m_int, e, N)
    
    if c == c_verify:
        print("  ✓ 验证通过")
    else:
        print("  ✗ 验证失败")
    
    print("\n" + "="*70)
    print(f"Flag: {plaintext}")
    print("="*70)
    
    return plaintext
​
# 题目数据(示例,实际题目中N、e、c是完整给出的)
N = 98579...  # 替换为题目中的实际N值
e = 65537
c = 12345...  # 替换为题目中的实际c值
​
# 解题
if __name__ == "__main__":
    flag = solve_rsa(N, e, c)
    print(f"\n最终答案: {flag}")

六、知识点总结

6.1 RSA算法核心公式

操作 公式 说明
密钥生成 N = p × q 两个大素数乘积
φ(N) = (p-1)(q-1) 欧拉函数
d = e^(-1) mod φ(N) 私钥计算
加密 c = m^e mod N 用公钥加密
解密 m = c^d mod N 用私钥解密

6.2 破解RSA的关键

破解RSA = 分解N = 找出p和q
        ↓
有了p和q → 计算φ(N) → 计算私钥d → 解密密文

6.3 N的长度与安全性

N的长度 分解难度 安全性 说明
256位 容易 ❌ 不安全 个人电脑秒级分解
512位 较易 ❌ 不安全 云服务器分钟级分解
1024位 困难 ⚠️ 不推荐 专业团队数月分解
2048位 很难 ✅ 安全 当前技术不可行
4096位 极难 ✅✅ 非常安全 未来几十年安全

6.4 分解N的常用方法

  1. 试除法:适合小N(<100位)

  2. Pollard rho:适合中等N(100-200位)

  3. 二次筛法(QS):适合中大N(200-300位)

  4. 数域筛法(NFS):适合大N(300位以上)

  5. 在线工具:factordb.com(自动选择最优方法)


七、思考与拓展

7.1 为什么这个题能快速分解?

因为N只有256位(77位十进制),对于现代计算机:

  • 个人笔记本:几秒钟

  • 云服务器:毫秒级

  • 在线工具:瞬间

7.2 如果N是2048位怎么办?

如果N是2048位,直接分解不可行。需要找其他弱点:

可能的攻击方法

  • 小公钥e攻击(e=3)

  • 共模攻击(多组密文使用相同N)

  • 费马分解(p和q接近)

  • Wiener攻击(d很小)

  • Pollard p-1攻击(p-1或q-1有小因子)

7.3 相关题目推荐

如果你觉得这道题太简单,可以挑战:

  1. RSA with Small Exponent(CryptoHack)

    • 难度:⭐⭐⭐

    • 考点:小公钥指数攻击

  2. Common RSA(CryptoHack)

    • 难度:⭐⭐⭐⭐

    • 考点:共模攻击

  3. Mini RSA(PicoCTF)

    • 难度:⭐⭐⭐

    • 考点:小模数分解


八、常见问题解答

Q1:为什么使用65537作为公钥e?

A:65537 = 2^16 + 1,是一个费马素数。优点:

  • 不太小(避免小指数攻击)

  • 不太大(加密效率高)

  • 二进制只有两个1(计算快)

Q2:φ(N)是什么?

A:φ(N)是欧拉函数,表示小于N且与N互质的正整数个数。对于N = p × q:

φ(N) = φ(p × q) = (p-1)(q-1)

Q3:为什么pow(c, d, N)这么快?

A:Python的pow()函数使用了快速幂算法(平方-乘算法),时间复杂度O(log d)。

# 普通幂运算(慢)
result = c ** d % N  # O(d)
​
# 快速幂运算(快)
result = pow(c, d, N)  # O(log d)

Q4:factordb.com是什么原理?

A:factordb.com维护了一个大整数分解数据库:

  • 已知的分解结果会被缓存

  • 新的分解请求会使用最优算法

  • 对于小整数,瞬间返回结果


九、参考资料

  1. RSA算法原理

  2. 在线分解工具

  3. CTF平台

  4. Python密码学库


十、总结

这道题虽然简单,但它展示了RSA破解的核心原理:

✓ 小N → 可分解 → 私钥可恢复 → 密文可解密

关键知识点:

  1. RSA的安全性依赖于大数分解的困难性

  2. 小模数N是不安全的,可以被快速分解

  3. 分解N是破解RSA的最直接方法

  4. 使用足够长度的密钥(2048位以上)

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐