2. RSA算法原理
RSA算法是1977年由Ron Rivest、Adi Shamir和Leonard Adleman共同提出的非对称加密算法,其安全性基于大整数因子分解的数学困难问题。RSA是当前应用最广泛的公钥密码体制之一,被用于SSL/TLS、数字签名、安全电子邮件等场景。
2.1 RSA密钥生成
RSA密钥生成的核心步骤包括大素数选取、模数计算、公私钥指数确定。以下为完整流程:
| 步骤 | 操作 | 数学表达 | 说明 |
|---|---|---|---|
| 1 | 选取两个大素数 | p, q | 通常为512~2048位随机素数,需通过Miller-Rabin素性测试 |
| 2 | 计算模数 | n = p × q | n的二进制长度即为RSA密钥长度(如2048位) |
| 3 | 计算欧拉函数 | φ(n) = (p-1)(q-1) | 用于后续指数互素条件判断 |
| 4 | 选取公钥指数 | e | 满足 1 < e < φ(n) 且 gcd(e, φ(n)) = 1,常用值 65537 |
| 5 | 计算私钥指数 | d ≡ e⁻¹ mod φ(n) | 使用扩展欧几里得算法求解乘法逆元 |
| 6 | 输出密钥对 | 公钥 (n, e),私钥 (n, d) | p, q, φ(n) 必须安全销毁或保密存储 |
工程实践提示:公钥指数 e 通常固定为 65537(0x10001),因其二进制表示中只有两个1,可大幅提升加密运算效率,同时保持足够的安全性。
2.2 大素数选取与素性测试
大素数选取是RSA密钥生成的第一步,也是最耗时的步骤。实际工程中采用随机生成+概率素性测试的策略:
- 随机生成候选数:使用密码学安全随机数生成器(CSPRNG)生成长度为 k 位的奇数
- 小素数筛除:用前几百个小素数(如3,5,7,11,...)快速筛除合数,减少后续计算量
- Miller-Rabin素性测试:对通过筛除的候选数进行多次测试,错误概率 ≤ 4-t(t为测试轮数)
安全参数建议:对于2048位RSA密钥,p和q各约1024位。Miller-Rabin测试轮数推荐为64轮(NIST SP 800-89标准),此时合数被误判为素数的概率低于 2-128。
2.3 加密与解密过程
RSA加密和解密基于模幂运算,将明文/密文视为整数,在模 n 下进行指数运算。消息 m 必须满足 0 ≤ m < n。
| 操作 | 公式 | 输入 | 输出 |
|---|---|---|---|
| 加密 | c = me mod n | 明文 m,公钥 (n, e) | 密文 c |
| 解密 | m = cd mod n | 密文 c,私钥 (n, d) | 明文 m |
解密正确性由欧拉定理保证:若 c ≡ me (mod n),则 cd ≡ med ≡ m1+kφ(n) ≡ m (mod n)。
2.4 RSA的安全性分析
RSA的安全性建立在以下数学难题之上,同时需防范多种攻击向量:
2.4.1 安全基础
- 大整数因子分解问题(IFP):给定 n = p×q,求 p 和 q。目前没有多项式时间算法,2048位RSA在可预见的未来被认为是安全的
- RSA问题(RSAP):给定 (n, e, c),求 m 使得 c ≡ me mod n。若IFP可解,则RSAP可解;反之不一定成立
2.4.2 主要攻击类型与防御
| 攻击类型 | 攻击原理 | 防御措施 |
|---|---|---|
| 因子分解攻击 | 使用数域筛法(NFS)分解 n | 密钥长度 ≥ 2048位;p和q位长相近但差值足够大 |
| 共模攻击 | 同一 n 下不同 e 加密同一消息 | 不同用户使用不同 n,禁止共享模数 |
| 低指数攻击 | e 过小(如 e=3)且 m 较小时可开方 | 使用 e=65537;对消息进行OAEP填充 |
| 时序攻击 | 通过解密时间差异推断私钥位 | 使用常数时间模幂运算或盲化技术 |
| 选择密文攻击 | 利用解密预言机获取信息 | 使用OAEP/OAEP+填充方案,确保密文不可延展 |
安全警告:教科书式RSA(无填充)存在严重安全隐患,包括确定性加密、密文可延展性、小指数攻击等。实际应用中必须使用OAEP(最优非对称加密填充)或RSAES-PKCS1-v1_5等安全填充方案。
2.4.3 密钥长度安全等级
| 密钥长度(位) | 安全等级 | 推荐使用场景 | 预计安全年限 |
|---|---|---|---|
| 1024 | 已不安全 | 已废弃,不应使用 | — |
| 2048 | 当前标准 | 一般商业应用、TLS证书 | 至2030年 |
| 3072 | 长期安全 | 高安全需求、政府级应用 | 至2040年 |
| 4096 | 极高安全 | 根证书、长期密钥 | 至2050年+ |
性能权衡:密钥长度每增加一倍,加密/解密时间约增加6~8倍。2048位RSA在主流CPU上单次解密约需1~5ms,4096位则需5~30ms。建议在安全性与性能之间选择2048或3072位。
2.5 RSA算法代码示例
以下为Python实现的RSA密钥生成、加密与解密核心逻辑(仅用于教学演示,实际生产环境应使用cryptography库):
import random
from math import gcd
def generate_prime(bits):
"""生成指定位数的素数(简化版)"""
while True:
# 生成随机奇数
candidate = random.getrandbits(bits) | (1 << (bits-1)) | 1
# Miller-Rabin素性测试(简化:5轮)
if is_probable_prime(candidate, 5):
return candidate
def is_probable_prime(n, k):
"""Miller-Rabin素性测试"""
if n < 2: return False
if n == 2 or n == 3: return True
if n % 2 == 0: return False
r, d = 0, n - 1
while d % 2 == 0:
r += 1
d //= 2
for _ in range(k):
a = random.randrange(2, n - 2)
x = pow(a, d, n)
if x == 1 or x == n - 1:
continue
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
def rsa_keygen(bits=2048):
"""RSA密钥生成"""
# 选取两个大素数
p = generate_prime(bits // 2)
q = generate_prime(bits // 2)
# 计算模数
n = p * q
phi = (p - 1) * (q - 1)
# 公钥指数(常用65537)
e = 65537
# 确保互素
while gcd(e, phi) != 1:
e = random.randrange(3, phi, 2)
# 私钥指数(扩展欧几里得算法)
d = pow(e, -1, phi)
return (n, e), (n, d)
def rsa_encrypt(m, pub_key):
"""RSA加密"""
n, e = pub_key
return pow(m, e, n)
def rsa_decrypt(c, priv_key):
"""RSA解密"""
n, d = priv_key
return pow(c, d, n)
# 使用示例
pub_key, priv_key = rsa_keygen(2048)
message = 123456789 # 实际应使用填充后的消息
ciphertext = rsa_encrypt(message, pub_key)
plaintext = rsa_decrypt(ciphertext, priv_key)
print(f"原始消息: {message}")
print(f"解密结果: {plaintext}")
print(f"加解密正确: {message == plaintext}")
关键说明:上述代码中的
pow(m, e, n) 使用Python内置的模幂运算,采用平方-乘算法,时间复杂度为 O(log e) 次模乘运算。实际工程中应使用 cryptography.hazmat.primitives.asymmetric.rsa 等成熟库,并配合OAEP填充使用。