非对称加密基础:RSA算法原理
聊完对称加密,咱们来看看非对称加密。很多人觉得非对称加密很神秘,其实没那么玄乎。说白了,就是两把钥匙——公钥和私钥。公钥人人都能拿到,私钥只有你自己知道。这就好比你家的信箱:任何人都能往里面投信(用公钥加密),但只有你有钥匙能打开(用私钥解密)。
RSA 是其中最经典的一种。我当年刚接触密码学的时候,觉得 RSA 的数学推导特别头疼。后来在项目中真正用过几轮,才明白核心思想其实很直观。下面我把几个关键点拆开来聊。
RSA 的数学基础——就是个陷阱门函数
RSA 依赖一个核心观察:大整数分解很难。两个大质数相乘很容易,但反过来分解成两个质数就极其困难。这个不对称性就是整个算法的基石。
看一下具体的密钥生成步骤:
- 选两个大质数 p 和 q。通常 1024 位以上,2024 年建议至少 2048 位。
- 计算 n = p × q。n 的长度就是密钥长度。
- 计算欧拉函数 φ(n) = (p-1)(q-1)。
- 选一个公钥指数 e,要求 1 < e < φ(n),且 e 与 φ(n) 互质。常用 65537。
- 计算私钥指数 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 涉及大数模幂运算,计算量很大。
| 密钥长度 | 安全等级 | 推荐用途 | 性能(签名/秒) |
|---|---|---|---|
| 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: 涉及密钥分发、数字签名、不需要高性能的场景。
- 何时别用: 大数据量加密、实时性要求极高的场景(比如视频流)。
- 替代方案: ECC(椭圆曲线)同样是非对称,但密钥更短、性能更好。如果系统允许,我更倾向 ECC。不过 RSA 的生态成熟度最高,兼容性最好。
好,RSA 的基础就聊到这儿。下一块我们接着聊 ECC,看看椭圆曲线怎么把密钥长度砍到 256 位还更安全。嗯,但不是现在——你先消化好 RSA 的核心思想,后面对比起来才轻松。