3. 非对称加密算法基础(RSA, ECC)

说到非对称加密,我觉得它最迷人的地方就是——你不需要事先跟对方共享秘密。公钥随便发,私钥自己藏好。这个思路在十几年前我第一次接触时,真是眼前一亮。

3.1 RSA 算法核心

RSA 的名字来自三位发明者 Rivest、Shamir、Adleman。它的安全性基于一个大整数分解的难度。

说白了就是:给你两个大素数 p 和 q 相乘得到 n 很容易,但给你 n 反推出 p 和 q,难得很。这个“单向门”就是 RSA 的根基。

3.1.1 密钥生成过程

我习惯把流程拆成四步,方便记忆:

  1. 选两个大素数 p、q(一般 1024 位以上)
  2. 计算 n = p × q,φ(n) = (p-1)(q-1)
  3. 选一个公钥指数 e(常用 65537),满足 1 < e < φ(n) 且 gcd(e, φ(n))=1
  4. 计算私钥 d ≡ e⁻¹ mod φ(n)

公钥就是 (n, e),私钥就是 (n, d)。

我个人习惯: 生产环境中别自己造密钥对,用 OpenSSL 或 Java KeyStore 这类成熟工具。我曾见过有人手写随机数生成 p、q,结果随机源太弱,被同事用 Yafu 几分钟分解了……

3.1.2 加解密与签名

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

签名反过来:用私钥签,公钥验。本质都是模幂运算。

注意: RSA 不能直接加密大于 n 的消息。实际中会用混合加密——先用 RSA 加密对称密钥(如 AES 的 Key),再用对称密钥加密数据。我记得刚做 SDK 时没注意这个,给文件直接 RSA 加密,结果超过 256 字节的内容全崩了……

3.2 ECC 椭圆曲线密码

ECC 的优势在于:同等安全强度下,密钥比 RSA 短得多。举个例子:256 位 ECC 的安全性与 3072 位 RSA 相当,但计算量却小很多。

它的数学基础是椭圆曲线上的离散对数问题。嗯,这里懂不懂方程不重要,你只要明白:椭圆曲线上点与点的加法运算构成一个群,正向算容易,反向(已知 G 和 kG 求 k)难得很。

3.2.1 密钥生成

  • 选定一条椭圆曲线(如 SM2 国密曲线、secp256k1 比特币曲线)
  • 选取基点 G,阶为 n
  • 私钥 d:随机取 1..n-1 之间的整数
  • 公钥 Q:Q = d × G(椭圆曲线上的点乘)

3.2.2 ECDH 与 ECDSA

ECDH(密钥协商):双方用自己的私钥和对方的公钥做点乘,得到相同的共享密钥。

ECDSA(数字签名):签名过程需要随机数 k,这一点极其重要。我曾经在调试一个国密库时,发现签名每次都用固定 k,结果被导出了私钥。

避坑指南: ECDSA 的随机数 k 必须真随机且永不重复。如果你在嵌入式设备上做签名,务必使用硬件真随机数发生器,不要用 rand()。重复 k 会导致私钥直接泄露,这是数学上证明的。

3.3 RSA vs ECC 对比

对比项 RSA ECC
数学基础 大整数分解 椭圆曲线离散对数
同等安全位数 3072 位 256 位
密钥生成速度 较慢(尤其大素数筛选) 快(只需随机点乘)
加解密/签名性能 加密/验签快,解密/签名慢 双向都比 RSA 快(除签名外小量慢于 RSA 验签)
典型应用 HTTPS 证书、旧协议 移动端、IoT、国密 SM2
量子抗性 无 无(但同等强度下更轻量)

你想想看,为什么越来越多的新系统用 ECC?说白了,手机处理器算力有限,ECC 能省电省带宽。但 RSA 在证书生态里更成熟,短时间内也不会消失。

3.4 实战中的选择建议

  • 新项目推荐 ECC(如 P-256 或 SM2)。 我在 IoT 设备开发中用过 secp256r1,跑在 Cortex-M4 上,ECDH 握手只要几十毫秒,RSA 根本做不到。
  • 兼容老系统时保留 RSA。 比如很多 CA 根证书还是 RSA 2048,你换掉可能客户端报错。
  • 签名场景注意哈希算法。 RSA 通常搭配 SHA-256,ECC 签名长度固定(如 P-256 签名为 64 字节),别搞混编码。

我建议: SDK 中同时支持两种算法,但默认使用 ECC。暴露配置项让用户选。另外签名验签一定要做公钥校验,防止恶意注入无效曲线造成的攻击。

非对称加密知识体系 非对称加密 RSA 大整数分解 密钥生成 加解密/签名 ECC 椭圆曲线离散对数 点乘运算 ECDH/ECDSA 实战选择要点 新项目推荐 ECC(P-256 / SM2),兼容老系统保留 RSA ECDSA 签名需真随机数 k,防止私钥泄露 混合加密:RSA/ECC 加密对称密钥,对称加密数据

3.5 代码片段示范:ECC 密钥生成(Java)

下面是我在 SDK 中常用的写法,用 Bouncy Castle 库,注意设置 Provider:

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.security.spec.*;

public class ECCKeyGen {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    public static KeyPair generateP256KeyPair() throws Exception {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
        ECGenParameterSpec ecSpec = new ECGenParameterSpec("P-256");
        kpg.initialize(ecSpec, new SecureRandom());
        return kpg.generateKeyPair();
    }

    public static void main(String[] args) throws Exception {
        KeyPair kp = generateP256KeyPair();
        System.out.println("私钥格式: " + kp.getPrivate().getAlgorithm());
        System.out.println("公钥格式: " + kp.getPublic().getAlgorithm());
    }
}

注意这里的 SecureRandom 必须用强随机数生成器。我在 Android 上踩过坑,某些低版本设备默认的 SecureRandom 不够随机,导致 ECDH 协商被预测。建议大家显示指定 new SecureRandom() 并在 Linux 上确保 /dev/urandom 可用。

3.6 小结

非对称加密是整个加密 SDK 的骨架。RSA 成熟稳定但笨重,ECC 轻快灵活但实现细节多。记住两个核心:密钥对的生成要真随机,私钥存储要用硬件安全模块或 TEE。好了,这一章就讲这些。下一节我们要进入对称加密与哈希的联合运用,到时候结合混合加密再深入聊。

注意事项: 无论用 RSA 还是 ECC,都不要自己实现数学运算。调用标准库或经过 FIPS 140-2 认证的 SDK。我曾见过有人手写椭圆曲线点乘,结果常数时间没做,被侧信道攻击提取了私钥。