3. 非对称加密基础:RSA、ECC算法原理,公钥与私钥的数学关系

聊到密钥保护,绕不开非对称加密。说实话,我刚入行那会儿,总觉得对称加密又快又简单,干嘛要搞这么复杂的数学?直到有一次,我需要给分布在全国的几百台服务器分发会话密钥——用对称加密的话,每台服务器都得单独配一个密钥,管理起来简直是噩梦。从那以后,我才真正体会到非对称加密的价值。

非对称加密的核心思想,说白了就是:加密和解密用不同的钥匙。一把公开,一把私藏。公钥加密的数据,只有私钥能解开;反过来,私钥签名的东西,公钥可以验证。这种设计,让密钥分发问题迎刃而解。

核心概念速览

  • 公钥:公开给所有人,用于加密或验签
  • 私钥:自己保管,用于解密或签名
  • 数学基础:单向函数——正向计算容易,逆向推导极难

3.1 RSA算法:大整数分解的难题

RSA 是应用最广泛的非对称算法。它的安全性,建立在一个简单但深刻的事实上:两个大质数相乘很容易,但把乘积分解回质数却极其困难。

我记得有一次做安全审计,客户问:“RSA 的密钥对到底是怎么生成的?” 我现场给他画了个流程图。嗯,这里我把这个流程整理出来,你一看就明白。

RSA密钥生成与加解密流程 RSA 密钥生成与加解密流程 密钥生成阶段 选择大质数 p, q n = p × q φ(n) = (p-1)(q-1) 选择 e (1<e<φ(n)) d = e⁻¹ mod φ(n) 公钥 (n, e) 私钥 (n, d) 加解密阶段 加密:c = mᵉ mod n (m: 明文, c: 密文) 解密:m = cᵈ mod n (d: 私钥指数) 💡 安全性核心:已知 n 求 p、q 是数学难题 RSA-2048 的 n 有 617 位十进制数,暴力分解需要上亿年 实际应用中,p、q 至少 512 位,且不能太接近

3.2 RSA 的数学细节

上面那张图把流程画清楚了。我再补充几个关键点,这些都是我在项目中踩过的坑。

3.2.1 密钥长度怎么选?

我见过不少团队为了性能,用 1024 位的 RSA。说实话,现在 1024 位已经不安全了。2015 年就有研究团队成功分解了 768 位的 RSA 模数。我个人建议:新系统至少用 2048 位,敏感场景用 4096 位。

密钥长度 安全等级 适用场景 性能(相对)
1024 位 已不安全 仅兼容旧系统 1x
2048 位 安全(2030年前) 通用场景 3-4x
4096 位 高安全 CA根证书、金融 10-15x

3.2.2 填充方案不能省

你可能会想:直接拿公钥加密明文不就行了?千万别。RSA 是确定性算法——同样的明文和公钥,每次加密结果都一样。攻击者可以穷举猜测。所以必须加随机填充。

我曾经踩过的坑: 早期项目用了 PKCS#1 v1.5 填充,结果被 Bleichenbacher 攻击打穿了。攻击者通过观察服务器返回的“填充错误”信息,一步步还原出明文。后来我全部迁移到了 OAEP 填充(Optimal Asymmetric Encryption Padding),才彻底解决这个问题。

实用建议: 用 OpenSSL 生成 RSA 密钥对时,记得指定 -aes256 保护私钥文件。另外,私钥文件权限一定要设为 600,别问我怎么知道的……

3.3 ECC 算法:椭圆曲线上的密码学

ECC(Elliptic Curve Cryptography)是 RSA 的强力竞争者。它的核心优势是:更短的密钥,同等的安全强度。256 位的 ECC 密钥,安全性和 3072 位的 RSA 相当。

ECC 的数学基础是椭圆曲线上的离散对数问题。简单说:在椭圆曲线上,已知点 P 和整数 k,计算 Q = kP 很容易;但已知 P 和 Q,求 k 却非常困难。这就是 ECC 的单向函数。

3.3.1 椭圆曲线长什么样?

标准椭圆曲线方程是 y² = x³ + ax + b。配上一个大素数 p,就构成了有限域上的椭圆曲线群。比特币用的 secp256k1 曲线,参数是 a=0, b=7。

# 椭圆曲线上的点加法(简化版)
# 给定曲线上两点 P、Q,求 R = P + Q

def point_add(P, Q, a, p):
    if P is None:
        return Q
    if Q is None:
        return P
    
    x1, y1 = P
    x2, y2 = Q
    
    # 计算斜率
    if x1 == x2 and y1 == y2:
        # 点倍运算
        s = (3 * x1 * x1 + a) * pow(2 * y1, -1, p) % p
    else:
        # 点加运算
        s = (y2 - y1) * pow(x2 - x1, -1, p) % p
    
    # 计算新点坐标
    x3 = (s * s - x1 - x2) % p
    y3 = (s * (x1 - x3) - y1) % p
    
    return (x3, y3)

3.4 RSA vs ECC:怎么选?

这个问题我经常被问到。说实话,没有银弹。我根据项目经验,给你一个对比表:

对比维度 RSA ECC
密钥长度(等效安全) 2048 位 256 位
加密速度 慢(指数运算) 快(点乘运算)
签名速度 慢 快
验签速度 快 慢(相对 RSA 慢)
密钥生成 慢(需要找大质数) 快(随机数即可)
抗量子攻击 弱 弱(Shor算法通杀)
标准化程度 极高(PKCS#1) 高(SECG、NIST)

我的选择建议:

  • 新系统优先用 ECC:密钥短、性能好、省带宽。TLS 1.3 已经默认用 ECC 做密钥交换了。
  • 兼容性要求高用 RSA:有些老旧设备只支持 RSA,这时候别硬上 ECC。
  • 混合使用:我现在的项目,TLS 握手用 ECDHE 做密钥交换,证书签名用 RSA 保证兼容性。各取所长。

3.5 公钥与私钥的数学关系

很多人觉得非对称加密很神秘。其实核心就一句话:公钥和私钥是数学上配对的一对数值。用公钥加密的数据,只有配对的私钥能解开;反过来,私钥签名的数据,只有配对的公钥能验证。

在 RSA 中,这种配对关系体现在模逆运算上:e × d ≡ 1 (mod φ(n))。在 ECC 中,私钥是一个随机整数 k,公钥是曲线上的点 Q = kG(G 是基点)。

重要提醒: 私钥一旦泄露,整个安全体系就崩塌了。我见过一个案例,某公司把私钥硬编码在移动 App 里,结果被反编译后直接提取出来。攻击者用这个私钥伪造了所有 API 请求的签名。所以,私钥必须放在安全硬件(HSM)或可信执行环境(TEE)中,绝不能出现在代码里。

3.6 实战:用 OpenSSL 生成密钥对

光说不练假把式。我带你走一遍实际生成密钥对的命令。这些命令我几乎每天都会用到。

# 生成 RSA 2048 位私钥
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
  -aes-256-cbc -out private_rsa.pem

# 从私钥提取公钥
openssl pkey -in private_rsa.pem -pubout -out public_rsa.pem

# 生成 ECC P-256 私钥
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 \
  -aes-256-cbc -out private_ecc.pem

# 从私钥提取公钥
openssl pkey -in private_ecc.pem -pubout -out public_ecc.pem

# 查看密钥详情
openssl pkey -in private_rsa.pem -text -noout
openssl pkey -in private_ecc.pem -text -noout

小技巧: 生成私钥时加上 -aes-256-cbc 参数,OpenSSL 会用你输入的密码加密私钥文件。这样即使文件泄露,没有密码也打不开。另外,我习惯把公钥文件名命名为 *.pub,一目了然。