RSA算法实战:从数学原理到Python代码实现(附常见攻击防御指南)

如果你在开发一个需要用户登录的Web应用,或者设计一套需要安全传输数据的系统,那么你迟早会遇到一个名字:RSA。这个诞生于1977年的算法,至今仍然是互联网安全的基石之一。它保护着我们的HTTPS连接、数字签名、软件更新,甚至加密货币交易。但很多开发者对它既熟悉又陌生——知道它很重要,却总觉得那些数学公式和密钥对生成过程像是一团迷雾。

这篇文章就是为你准备的。我们不打算只停留在“公钥加密,私钥解密”的口号式理解上,而是要亲手用Python代码,一步步实现RSA的完整流程。更重要的是,我们会深入那些教科书里常常一笔带过,但在实战中却至关重要的细节:为什么参数的选择如此关键?哪些常见的实现错误会让看似坚固的RSA变得不堪一击?攻击者究竟是如何利用这些漏洞的?理解了这些,你才能真正在项目中安全地使用RSA,而不是盲目地调用一个加密库。

1. 核心数学原理:为什么RSA是可靠的?

在写第一行代码之前,我们必须先搞清楚RSA背后的数学逻辑。放心,我们不会陷入过于抽象的纯数学推导,而是聚焦于那些直接影响算法实现和安全的几个核心概念。

1.1 模运算:一切的基础

RSA的整个加解密过程都发生在“模运算”的世界里。你可以把它想象成一个时钟。在模12的时钟上,15点就是3点,因为15除以12余3。我们写作 15 ≡ 3 (mod 12)。在RSA中,我们处理的是一个巨大的“时钟”,其模数是一个大整数N。

模运算有几个关键性质,它们保证了RSA的可逆性:

  • (a * b) mod N = [(a mod N) * (b mod N)] mod N
  • a^e mod N 可以通过重复的平方乘算法高效计算,即使e很大。

一个直观的例子:计算 7^13 mod 10。直接计算7的13次方是个天文数字,但利用模运算性质:

7^1 mod 10 = 7
7^2 mod 10 = 49 mod 10 = 9
7^4 mod 10 = (7^2)^2 mod 10 = 9^2 mod 10 = 81 mod 10 = 1
7^8 mod 10 = (7^4)^2 mod 10 = 1^2 mod 10 = 1
那么 7^13 = 7^8 * 7^4 * 7^1 ≡ 1 * 1 * 7 ≡ 7 (mod 10)

这个“平方-乘”算法是高效实现RSA幂运算的核心。

1.2 欧拉定理与密钥对的生成

RSA的安全性建立在“大整数分解难题”上,而其正确性则依赖于欧拉定理。对于任意与N互质的整数a,有: a^φ(N) ≡ 1 (mod N) 其中φ(N)是欧拉函数,表示小于N且与N互质的正整数的个数。如果N是两个质数p和q的乘积,那么φ(N) = (p-1)*(q-1)

注意:这里的“互质”是关键。如果明文m与N不互质(即m是p或q的倍数),欧拉定理不能直接应用,但RSA仍然成立——这需要额外的证明,也是很多教科书省略的部分。在实际使用中,由于p和q极大,随机选到与N不互质的m的概率微乎其微,但理论完备性很重要。

密钥生成的核心步骤,就是找到两个特殊的指数e和d,使得: e * d ≡ 1 (mod φ(N)) 这意味着 e * d = k * φ(N) + 1 对于某个整数k成立。

那么加解密过程 (m^e)^d mod N 就变成了 m^(e*d) mod N = m^(k*φ(N)+1) mod N。根据欧拉定理,如果m与N互质,m^(φ(N)) ≡ 1 mod N,所以上式等于 m mod N。即使m与N不互质,通过中国剩余定理也能证明等式成立。这就是RSA能够正确解密的数学保证。

2. 手把手实现RSA密钥生成与加解密

理论足够清晰了,现在让我们打开Python环境,从零开始实现它。我们将使用Python内置的randommath库,以及处理大整数的能力。

2.1 生成大质数:Miller-Rabin素性测试

RSA的第一步是生成两个大质数p和q。如何判断一个上百位的大数是质数?确定性算法太慢,我们使用概率性的Miller-Rabin素性测试,它速度快且出错概率极低。

import random
import math

def is_prime(n, k=5):
    """使用Miller-Rabin算法进行素性测试"""
    if n < 2: return False
    for p in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]:
        if n % p == 0:
            return n == p
    
    # 将n-1写成 d * 2^s 的形式
    s, d = 0, n - 1
    while d % 2 == 0:
        s += 1
        d //= 2
    
    # 进行k轮测试
    for _ in range(k):
        a = random.randrange(2, n - 1)
        x = pow(a, d, n)
        if x == 1 or x == n - 1:
            continue
        for _ in range(s - 1):
            x = pow(x, 2, n)
            if x == n - 1:
                break
        else:
            return False
    return True

def generate_large_prime(bit_length=512):
    """生成指定位数的大质数"""
    while True:
        # 确保是奇数,且高位为1以保证位数
        candidate = random.getrandbits(bit_length) | (1 << (bit_length - 1)) | 1
        if is_prime(candidate):
            return candidate

这里k=5意味着进行5轮独立测试,每轮通过的概率至少3/4,5轮都误判为质数的概率小于(1/4)^5 ≈ 0.00098,对于实际应用已经足够安全。

2.2 完整的密钥生成流程

有了质数生成函数,我们现在可以生成完整的RSA密钥对:

def extended_gcd(a, b):
    """扩展欧几里得算法,返回(g, x, y)使得 a*x + b*y = g = gcd(a, b)"""
    if a == 0:
        return b, 0, 1
    g, x1, y1 = extended_gcd(b % a, a)
    x = y1 - (b // a) * x1
    y = x1
    return g, x, y

def mod_inverse(e, phi):
    """计算e关于模phi的模逆元d,即 e*d ≡ 1 (mod phi)"""
    g, x, _ = extended_gcd(e, phi)
    if g != 1:
        raise ValueError("e和φ(n)必须互质")
    return x % phi

def generate_rsa_keys(bit_length=1024):
    """生成RSA公钥和私钥"""
    print("正在生成大质数p...")
    p = generate_large_prime(bit_length // 2)
    print("正在生成大质数q...")
    q = generate_large_prime(bit_length // 2)
    
    n = p * q
    phi = (p - 1) * (q - 1)
    
    # 选择公钥指数e,通常用65537(2^16+1)
    e = 65537
    while math.gcd(e, phi) != 1:
        # 如果65537与φ(n)不互质(极罕见),尝试其他值
        e = 65539
    
    # 计算私钥指数d
    d = mod_inverse(e, phi)
    
    # 返回公钥(n, e)和私钥(n, d)
    public_key = (n, e)
    private_key = (n, d)
    
    print(f"密钥生成完成!")
    print(f"n的长度: {n.bit_length()} 位")
    print(f"p: {p}")
    print(f"q: {q}")
    print(f"φ(n): {phi}")
    print(f"公钥e: {e}")
    print(f"私钥d: {d}")
    
    return public_key, private_key, p, q

为什么常用e=65537? 这是一个精心选择的值:

  1. 二进制表示只有两个1(10000000000000001),使得平方乘运算非常高效
  2. 足够大,避免了低加密指数攻击
  3. 与大多数φ(n)互质
  4. 不是费马数,避免了某些特殊攻击

2.3 加解密与数字签名实现

现在实现核心的加解密函数。注意,RSA本身只能加密比N小的整数,所以实际应用中需要对长数据进行分块,并配合填充方案(如OAEP)。

def rsa_encrypt(message_int, public_key):
    """RSA加密:c = m^e mod n"""
    n, e = public_key
    if message_int >= n:
        raise ValueError("明文必须小于n")
    return pow(message_int, e, n)

def rsa_decrypt(cipher_int, private_key):
    """RSA解密:m = c^d mod n"""
    n, d = private_key
    return pow(cipher_int, d, n)

def rsa_sign(message_int, private_key):
    """RSA签名:s = m^d mod n"""
    return rsa_decrypt(message_int, private_key)  # 数学上相同

def rsa_verify(signature_int, message_int, public_key):
    """RSA验证:验证 m ≡ s^e mod n"""
    n, e = public_key
    return message_int == pow(signature_int, e, n)

# 演示完整流程
def demo_basic_rsa():
    print("=== RSA完整流程演示 ===")
    
    # 1. 生成密钥
    public_key, private_key, p, q = generate_rsa_keys(bit_length=256)  # 测试用256位,实际应用至少2048位
    n, e = public_key
    _, d = private_key
    
    # 2. 准备明文(转换为整数)
    message = "Hello RSA!"
    message_bytes = message.encode('utf-8')
    message_int = int.from_bytes(message_bytes, 'big')
    
    print(f"\n原始消息: '{message}'")
    print(f"消息整数: {message_int}")
    print(f"消息长度: {len(message_bytes)} 字节")
    
    # 3. 加密(确保m < n)
    if message_int >= n:
        print("警告:消息太长,需要分块加密")
        # 实际应用中这里应该分块
        return
    
    cipher_int = rsa_encrypt(message_int, public_key)
    print(f"\n加密后的密文: {cipher_int}")
    
    # 4. 解密
    decrypted_int = rsa_decrypt(cipher_int, private_key)
    decrypted_bytes = decrypted_int.to_bytes((decrypted_int.bit_length() + 7) // 8, 'big')
    decrypted_message = decrypted_bytes.decode('utf-8', errors='ignore').rstrip('\x00')
    
    print(f"解密后的整数: {decrypted_int}")
    print(f"解密后的消息: '{decrypted_message}'")
    
    # 5. 签名与验证
    print(f"\n=== 数字签名演示 ===")
    signature = rsa_sign(message_int, private_key)
    print(f"消息的签名: {signature}")
    
    is_valid = rsa_verify(signature, message_int, public_key)
    print(f"签名验证结果: {'有效' if is_valid else '无效'}")
    
    # 尝试篡改后验证
    tampered_message_int = message_int + 1
    is_valid_tampered = rsa_verify(signature, tampered_message_int, public_key)
    print(f"篡改后验证结果: {'有效(不应该!)' if is_valid_tampered else '无效(正确)'}")

if __name__ == "__main__":
    demo_basic_rsa()

运行这段代码,你会看到RSA从密钥生成到加解密、签名的完整过程。但请注意,这个实现是教学用的简化版本,实际生产环境需要考虑更多因素:

生产环境考虑 教学版本缺失 风险
填充方案 无填充(教科书式RSA) 确定性加密,易受选择明文攻击
密钥存储 内存中明文存储 私钥泄露风险
错误处理 简单异常抛出 可能通过错误信息泄露密钥信息
侧信道防护 可能通过时间、功耗分析恢复密钥

3. 常见攻击原理与防御实战

理解了RSA的基本实现后,我们来看看攻击者会从哪些角度突破它。只有知道攻击如何发生,才能更好地防御。

3.1 低加密指数攻击与广播攻击

攻击场景:当加密指数e很小时(比如e=3),如果明文m也比较小,使得 m^e < n,那么加密操作 c = m^e mod n 实际上就是 c = m^e(因为没超过模数n)。攻击者可以直接对密文c开e次方恢复明文。

def low_exponent_attack(ciphertext, e):
    """低加密指数攻击演示"""
    import gmpy2
    
    # 尝试直接开e次方
    m, is_exact = gmpy2.iroot(ciphertext, e)
    if is_exact:
        return int(m)
    return None

# 防御措施:总是使用适当的填充(如OAEP)
# 填充后,即使m很小,填充后的消息也会很大,确保 m^e > n

更隐蔽的是低加密指数广播攻击:如果同一个明文m用相同的低指数e加密,但不同的模数n1, n2, n3...发送给多个接收者,攻击者可以利用中国剩余定理恢复明文。

def chinese_remainder_theorem(n, a):
    """中国剩余定理求解同余方程组 x ≡ a_i (mod n_i)"""
    from functools import reduce
    
    def egcd(a, b):
        if b == 0:
            return 1, 0, a
        x, y, g = egcd(b, a % b)
        return y, x - (a // b) * y, g
    
    total = 0
    prod = reduce(lambda x, y: x * y, n)
    
    for n_i, a_i in zip(n, a):
        p = prod // n_i
        _, inv, _ = egcd(p, n_i)
        total += a_i * inv * p
    
    return total % prod

def broadcast_attack(ciphertexts, moduli, e=3):
    """低加密指数广播攻击演示"""
    # ciphertexts[i] = m^e mod moduli[i]
    # 使用中国剩余定理计算 m^e mod (n1*n2*n3...)
    m_e = chinese_remainder_theorem(moduli, ciphertexts)
    
    # 现在 m^e < n1*n2*n3*...,可以直接开e次方
    return low_exponent_attack(m_e, e)

防御策略

  1. 永远不要使用e=3,至少使用e=65537
  2. 使用随机填充:PKCS#1 v1.5或OAEP填充确保每次加密的输入都不同且足够大
  3. 避免重复加密:同一消息用不同密钥加密时,确保填充随机化

3.2 共模攻击

攻击场景:两个用户使用相同的模数n但不同的指数e1、e2,并且加密了同一个明文m。如果e1和e2互质,攻击者可以恢复明文。

数学原理:存在整数s、t使得 e1*s + e2*t = 1(贝祖等式)。那么:

c1^s * c2^t ≡ (m^e1)^s * (m^e2)^t ≡ m^(e1*s + e2*t) ≡ m^1 ≡ m (mod n)
def common_modulus_attack(c1, c2, e1, e2, n):
    """共模攻击演示"""
    import gmpy2
    
    # 使用扩展欧几里得算法求s, t
    g, s, t = extended_gcd(e1, e2)
    
    if g != 1:
        raise ValueError("e1和e2必须互质")
    
    # 确保s和t为正数
    if s < 0:
        c1 = gmpy2.invert(c1, n)
        s = -s
    if t < 0:
        c2 = gmpy2.invert(c2, n)
        t = -t
    
    # 计算 m = c1^s * c2^t mod n
    part1 = pow(c1, s, n)
    part2 = pow(c2, t, n)
    m = (part1 * part2) % n
    
    return m

防御策略绝对不要共享模数n!每个用户必须生成自己独立的p和q。这是RSA使用中的铁律。

3.3 维纳攻击(小私钥d攻击)

攻击场景:当私钥d很小时(具体来说,d < n^0.25/3),攻击者可以从公钥(n, e)中恢复出私钥d。攻击基于连分数展开。

def wiener_attack(e, n):
    """维纳攻击:当d很小时从(e, n)恢复d"""
    import math
    
    # 将e/n展开为连分数
    def continued_fraction(x, y):
        cf = []
        while y:
            cf.append(x // y)
            x, y = y, x % y
        return cf
    
    # 计算连分数的收敛子
    def convergents(cf):
        convergents = []
        for i in range(1, len(cf) + 1):
            num, den = 1, 0
            for term in cf[:i][::-1]:
                num, den = den + term * num, num
            convergents.append((num, den))
        return convergents
    
    cf = continued_fraction(e, n)
    convergents_list = convergents(cf)
    
    for k, d in convergents_list:
        if k == 0:
            continue
        
        # 检查是否满足 ed ≡ 1 (mod φ(n))
        phi = (e * d - 1) // k
        
        # 从φ(n)和n求解p, q: n = p*q, φ(n) = (p-1)(q-1)
        # 解二次方程: x^2 - (n - φ(n) + 1)x + n = 0
        b = n - phi + 1
        discriminant = b * b - 4 * n
        
        if discriminant >= 0:
            sqrt_disc = int(math.isqrt(discriminant))
            if sqrt_disc * sqrt_disc == discriminant:
                p = (b + sqrt_disc) // 2
                q = (b - sqrt_disc) // 2
                if p * q == n:
                    return d, p, q
    
    return None

# 防御:确保d足够大,通常要求d > n^0.292(更安全的界限)
# 实际中,使用标准密钥生成方法(如选择e=65537)自然会产生足够大的d

维纳攻击的实战意义:这个攻击告诉我们,不能为了解密速度快而故意选择小的d。有些实现可能会尝试使用小的d来加速解密操作,但这是极其危险的。

3.4 计时攻击与防御

这是一种侧信道攻击,不直接攻击数学结构,而是通过测量解密操作的时间来推断私钥信息。

# 有漏洞的实现(易受计时攻击)
def vulnerable_decrypt(c, d, n):
    """有漏洞的解密实现:执行时间与d的比特相关"""
    result = 1
    base = c
    
    # 平方乘算法的朴素实现
    while d > 0:
        if d & 1:  # 如果当前位是1
            result = (result * base) % n  # 这个乘法耗时
        base = (base * base) % n  # 平方操作
        d >>= 1
    
    return result

# 防御版本:恒定时间实现
def constant_time_decrypt(c, d, n):
    """抗计时攻击的解密实现"""
    # 使用蒙哥马利幂模或 blinding 技术
    # 这里展示一个简单的盲化技术
    import random
    
    # 盲化因子
    r = random.randrange(2, n - 1)
    r_inv = pow(r, -1, n)  # Python 3.8+ 支持模逆
    
    # 盲化密文
    c_blinded = (c * pow(r, 65537, n)) % n  # 假设e=65537
    
    # 解密盲化后的密文
    m_blinded = pow(c_blinded, d, n)
    
    # 去除盲化
    m = (m_blinded * r_inv) % n
    
    return m

防御侧信道攻击的完整策略

攻击类型 防御措施
计时攻击 恒定时间算法、操作盲化
功耗分析 随机化指数、掩码技术
故障攻击 冗余计算、结果验证
缓存攻击 恒定内存访问模式

4. 生产环境最佳实践与常见陷阱

在真实项目中直接使用我们上面实现的“教科书RSA”是危险的。以下是必须注意的关键点。

4.1 填充方案:为什么PKCS#1 v1.5不够安全

教科书RSA是确定性的:同样的明文总是产生同样的密文。这会导致许多攻击:

  1. 选择明文攻击:攻击者可以猜测明文并验证猜测
  2. 语义安全性缺失:不能隐藏明文的模式

OAEP填充是目前推荐的标准:

def rsa_oaep_encrypt(message, public_key, label=b''):
    """RSA-OAEP加密(简化示意,实际应使用密码学库)"""
    import hashlib
    import os
    
    n, e = public_key
    k = (n.bit_length() + 7) // 8  # 模数n的字节长度
    h_len = 32  # SHA-256输出长度
    m_len = len(message)
    
    if m_len > k - 2 * h_len - 2:
        raise ValueError("消息太长")
    
    # 1. 生成随机种子
    seed = os.urandom(h_len)
    
    # 2. 使用MGF1生成数据掩码
    def mgf1(seed, length):
        # 掩码生成函数(简化)
        counter = 0
        output = b''
        while len(output) < length:
            C = seed + counter.to_bytes(4, 'big')
            output += hashlib.sha256(C).digest()
            counter += 1
        return output[:length]
    
    # 3. 构造数据块DB
    lhash = hashlib.sha256(label).digest()
    ps = b'\x00' * (k - m_len - 2 * h_len - 2)
    DB = lhash + ps + b'\x01' + message
    
    # 4. 应用掩码
    db_mask = mgf1(seed, k - h_len - 1)
    masked_db = bytes(x ^ y for x, y in zip(DB, db_mask))
    
    # 5. 生成种子掩码
    seed_mask = mgf1(masked_db, h_len)
    masked_seed = bytes(x ^ y for x, y in zip(seed, seed_mask))
    
    # 6. 编码后的消息
    em = b'\x00' + masked_seed + masked_db
    
    # 7. RSA加密
    m = int.from_bytes(em, 'big')
    c = pow(m, e, n)
    
    return c.to_bytes((c.bit_length() + 7) // 8, 'big')

重要对比

特性 教科书RSA PKCS#1 v1.5 OAEP
确定性加密 部分随机化 完全随机化
抵抗选择明文攻击 较弱
抵抗适应性选择密文攻击 是(在随机预言模型下)
标准推荐 绝不使用 遗留系统,逐步淘汰 新系统必须使用

4.2 密钥生成的安全参数

选择正确的密钥长度和参数至关重要:

def secure_key_generation_parameters():
    """安全密钥生成参数指南"""
    return {
        "密钥长度": {
            "2023年最低要求": "2048位",
            "新系统推荐": "3072位",
            "长期安全(10年以上)": "4096位",
            "绝对不安全的长度": "1024位及以下"
        },
        "质数p和q": {
            "长度": "应为密钥长度的一半",
            "差值": "|p-q|应足够大,避免费马分解",
            "强质数": "推荐但不强制,(p-1)和(q-1)应有大质因子",
            "生成算法": "使用安全的随机数生成器"
        },
        "公钥指数e": {
            "标准值": "65537 (0x10001)",
            "其他可接受值": "3(不推荐)、17、19等",
            "要求": "必须与φ(n)互质"
        },
        "私钥指数d": {
            "最小长度": "应大于n^0.292(防御维纳攻击)",
            "生成方式": "从e和φ(n)计算,不应人为选择"
        }
    }

4.3 实际部署中的常见错误

我在安全审计中经常看到这些错误:

  1. 硬编码密钥或弱随机数
# 错误示例
random.seed(12345)  # 可预测的随机数
p = generate_large_prime()  # 如果随机数种子固定,p总是相同

# 正确做法
import secrets
random.seed(secrets.token_bytes(32))  # 使用密码学安全的随机数
  1. 错误的重用
# 危险:同一密钥既用于加密又用于签名
def rsa_encrypt_and_sign(data, key):
    encrypted = rsa_encrypt(data, key)  # 使用私钥加密?不!
    signature = rsa_sign(data, key)     # 使用私钥签名
    return encrypted, signature

# 正确:加密用公钥,签名用私钥
def rsa_encrypt_and_sign_proper(data, public_key, private_key):
    encrypted = rsa_encrypt(data, public_key)  # 公钥加密
    signature = rsa_sign(data, private_key)    # 私钥签名
    return encrypted, signature
  1. 缺乏完整性保护
# 攻击者可以篡改密文,解密得到乱码但系统可能不检测
ciphertext = receive_from_network()
plaintext = rsa_decrypt(ciphertext, private_key)
process(plaintext)  # 如果密文被篡改,这里可能崩溃或产生错误输出

# 正确:加密时包含完整性校验
def encrypt_with_integrity(data, public_key):
    # 添加HMAC或使用认证加密模式
    hmac_key = secrets.token_bytes(32)
    encrypted_data = rsa_encrypt(data + hmac(data, hmac_key), public_key)
    encrypted_key = rsa_encrypt(hmac_key, public_key)
    return encrypted_data, encrypted_key

4.4 性能优化与大型系统考虑

当需要在大型系统中使用RSA时,性能成为关键考虑:

class OptimizedRSA:
    """优化版RSA实现,使用中国剩余定理加速解密"""
    
    def __init__(self, p, q, e=65537):
        self.p = p
        self.q = q
        self.n = p * q
        self.e = e
        
        # 预计算CRT参数
        self.dp = pow(e, -1, p - 1)  # d mod (p-1)
        self.dq = pow(e, -1, q - 1)  # d mod (q-1)
        self.qinv = pow(q, -1, p)     # q的模p逆元
        
    def decrypt_crt(self, c):
        """使用CRT加速解密,比直接计算 c^d mod n 快4倍"""
        # 分别在模p和模q下解密
        mp = pow(c % self.p, self.dp, self.p)
        mq = pow(c % self.q, self.dq, self.q)
        
        # 使用CRT组合结果
        h = (self.qinv * (mp - mq)) % self.p
        m = mq + h * self.q
        
        return m
    
    def encrypt(self, m):
        """加密(通常不需要优化,客户端操作)"""
        if m >= self.n:
            raise ValueError("明文过大")
        return pow(m, self.e, self.n)
    
# 性能对比
def performance_comparison():
    import time
    
    # 生成2048位密钥
    p = generate_large_prime(1024)
    q = generate_large_prime(1024)
    n = p * q
    e = 65537
    d = pow(e, -1, (p-1)*(q-1))
    
    rsa_crt = OptimizedRSA(p, q, e)
    
    # 测试消息
    m = 123456789
    c = pow(m, e, n)
    
    # 标准解密
    start = time.time()
    for _ in range(100):
        pow(c, d, n)
    std_time = time.time() - start
    
    # CRT解密
    start = time.time()
    for _ in range(100):
        rsa_crt.decrypt_crt(c)
    crt_time = time.time() - start
    
    print(f"标准解密时间: {std_time:.4f}秒")
    print(f"CRT解密时间: {crt_time:.4f}秒")
    print(f"加速比: {std_time/crt_time:.2f}倍")

实际部署建议

  1. 密钥类型选择

    • 短期会话密钥:考虑使用ECDSA(更短密钥,更快运算)
    • 长期身份证书:RSA 3072位或以上
    • 后量子安全:开始规划迁移到抗量子算法
  2. 混合加密系统

    def hybrid_encrypt(data, rsa_public_key):
        # 1. 生成随机的AES密钥
        aes_key = secrets.token_bytes(32)  # AES-256
        
        # 2. 用AES加密数据
        cipher = AES.new(aes_key, AES.MODE_GCM)
        ciphertext, tag = cipher.encrypt_and_digest(data)
        
        # 3. 用RSA加密AES密钥
        encrypted_key = rsa_encrypt(
            aes_key, 
            rsa_public_key,
            padding='OAEP'  # 必须使用填充!
        )
        
        return {
            'encrypted_key': encrypted_key,
            'ciphertext': ciphertext,
            'nonce': cipher.nonce,
            'tag': tag
        }
    
  3. 密钥轮换策略

    • 加密密钥:定期轮换(如每90天)
    • 签名密钥:生命周期更长,但需要明确的吊销机制
    • 根证书:可能数年不换,但必须有应急计划

RSA算法虽然已有四十多年历史,但它仍然是现代密码学的基石。理解其原理、实现细节和潜在陷阱,对于构建安全系统至关重要。真正的安全不在于使用最复杂的算法,而在于正确使用经过时间检验的算法。每次实现密码学功能时,问问自己:我是否真的需要自己实现?使用成熟的库(如Python的cryptography)通常是更安全的选择。但当理解背后的原理成为必须时,希望这篇文章能成为你探索之路上的可靠指南。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐