非对称加密基础:RSA算法原理

聊完对称加密,咱们来看看非对称加密。很多人觉得非对称加密很神秘,其实没那么玄乎。说白了,就是两把钥匙——公钥和私钥。公钥人人都能拿到,私钥只有你自己知道。这就好比你家的信箱:任何人都能往里面投信(用公钥加密),但只有你有钥匙能打开(用私钥解密)。

RSA 是其中最经典的一种。我当年刚接触密码学的时候,觉得 RSA 的数学推导特别头疼。后来在项目中真正用过几轮,才明白核心思想其实很直观。下面我把几个关键点拆开来聊。

RSA 的数学基础——就是个陷阱门函数

RSA 依赖一个核心观察:大整数分解很难。两个大质数相乘很容易,但反过来分解成两个质数就极其困难。这个不对称性就是整个算法的基石。

看一下具体的密钥生成步骤:

  1. 选两个大质数 p 和 q。通常 1024 位以上,2024 年建议至少 2048 位。
  2. 计算 n = p × q。n 的长度就是密钥长度。
  3. 计算欧拉函数 φ(n) = (p-1)(q-1)。
  4. 选一个公钥指数 e,要求 1 < e < φ(n),且 e 与 φ(n) 互质。常用 65537。
  5. 计算私钥指数 d,满足 e × d ≡ 1 (mod φ(n))。

公钥就是 (n, e),私钥就是 (n, d)。p 和 q 必须销毁或妥善保存。嗯,这里要注意:p 和 q 如果泄露,整个 RSA 就完了。我曾经在一个项目里看到有人把 p,q 写进配置文件还提交到 Git……那场面,真的无语。

核心公式:

  • 加密:c = me mod n
  • 解密:m = cd mod n

m 是明文整数,c 是密文整数。实际使用中,先要将字符串转换成一个大整数。

加密与解密——为什么能还原?

原理其实是一个数论定理。数学上可以证明:(me)d ≡ m (mod n)。只要 m 小于 n,就能正确还原。我刚开始做性能测试的时候,发现 RSA 加解密比 AES 慢好几个数量级。这很正常,因为 RSA 涉及大数模幂运算,计算量很大。

RSA 常用参数对比(2024 年安全基线)
密钥长度 安全等级 推荐用途 性能(签名/秒)
2048 位 128 位对称等效 一般业务签名、密钥交换 约 500-1000
3072 位 192 位对称等效 高安全场景 约 200-400
4096 位 256 位对称等效 关键基础设施、CA 根证书 约 100-200

别用太短的密钥。 512 位 RSA 在 1999 年就被破解了。现在 768 位也不安全。我建议所有新系统至少用 2048 位,能用 3072 位更好。别为了省那几毫秒性能,把安全性丢了。

签名与验签——RSA 的另一个重要用途

除了加密,RSA 还能做数字签名。流程刚好相反:

  • 签名: 用私钥对消息的哈希值计算签名。别人拿不到你的私钥,所以无法伪造。
  • 验签: 用公钥验证签名是否匹配。任何持有公钥的人都能验证消息来源是否合法。

我在做接口安全防护的时候,经常用 RSA 签名来保证请求未被篡改。对称加密只能防窃听,数字签名才能防抵赖。你想想看,如果没有签名,对方完全可以说“这个请求不是我发的”,你拿不出证据。

填充方案——不做填充就是裸奔

RSA 不能直接用原始数据做指数运算。为什么呢?因为明文空间有限制,而且直接运算会有数学上的弱点。比如同样的明文加密两次,结果一样,这就泄露了信息。

常见的填充方案有两种:

方案 特点 适用场景
PKCS#1 v1.5 简单,兼容性好 传统系统互操作
OAEP (PKCS#1 v2.0) 更安全,有可证明安全性 新系统首选
PSS(签名用) 概率签名,安全性高 数字签名推荐

我的习惯: 加密场景用 OAEP 填充,签名场景用 PSS 填充。PKCS#1 v1.5 虽然旧,但很多系统还在用。如果你必须兼容老系统,注意检查是否有 Bleichenbacher 攻击相关漏洞。我曾在升级一个支付网关时,发现对方只支持 v1.5 填充,当时花了两周做安全分析。

# Python 示例:RSA 加密与签名(使用 cryptography 库)
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding

# 生成密钥对
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048
)
public_key = private_key.public_key()

# 加密
message = b"字段级加密示例"
ciphertext = public_key.encrypt(
    message,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

# 解密
plaintext = private_key.decrypt(
    ciphertext,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

# 签名
signature = private_key.sign(
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)

# 验签
try:
    public_key.verify(
        signature,
        message,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    print("签名验证通过")
except:
    print("签名验证失败")

RSA 的局限——别盲目用它

RSA 不是万能药。最大的问题就是慢。加密少量数据还可以,要是加密几十 KB 的文件,速度就惨不忍睹。所以实际系统里,RSA 通常只加密对称密钥(比如 AES 的密钥),然后真正的大数据用对称加密来搞。这叫混合加密,后面我们专门会讲。

另外一个要注意的点:RSA 的密文长度等于密钥长度(按字节算就是 256 字节对应 2048 位)。加密同样长度的明文,对称加密几乎不膨胀,RSA 会膨胀到密钥长度。所以不要拿 RSA 直接加密长文本,开销太大。

踩坑提醒: 我曾经在一个移动端项目里直接用了 RSA 加密用户信息字段。结果每条记录加密后变成了 256 字节,数据库字段原本设计的是 varchar(128),上线当天就爆了。后来改成了先压缩再用 AES 加密,用 RSA 只加密 AES 密钥。性能提升 30 倍。

RSA 知识结构总览

下面这张图把 RSA 的核心环节串起来了。你可以看到密钥生成、加解密、签名验签是怎么关联的。

RSA 算法核心流程 密钥生成 p,q → n, φ(n), e, d 公钥 (n, e) 公开分发 私钥 (n, d) 安全保存 加密 c = m^e mod n 需 OAEP 填充 解密 m = c^d mod n 去填充得到明文 密文传输 数字签名 私钥签名 s = h(m)^d mod n 验签 公钥验签 h(m) == s^e mod n 安全性依赖于大整数分解困难;实际应用中 RSA 只加密/签名短数据(如对称密钥或哈希值) 密钥长度 ≥ 2048 位,填充方案推荐 OAEP(加密)/ PSS(签名)

你看,整个流程围绕公钥和私钥两条线展开。加密用公钥,解密用私钥;签名用私钥,验签用公钥。对称度非常高,容易记忆。但实际部署中要小心几个坑:密钥管理、填充方案选择、性能瓶颈。

小结几个硬指标

  • 何时用 RSA: 涉及密钥分发、数字签名、不需要高性能的场景。
  • 何时别用: 大数据量加密、实时性要求极高的场景(比如视频流)。
  • 替代方案: ECC(椭圆曲线)同样是非对称,但密钥更短、性能更好。如果系统允许,我更倾向 ECC。不过 RSA 的生态成熟度最高,兼容性最好。

好,RSA 的基础就聊到这儿。下一块我们接着聊 ECC,看看椭圆曲线怎么把密钥长度砍到 256 位还更安全。嗯,但不是现在——你先消化好 RSA 的核心思想,后面对比起来才轻松。