3. 非对称加密算法基础(RSA, ECC)
说到非对称加密,我觉得它最迷人的地方就是——你不需要事先跟对方共享秘密。公钥随便发,私钥自己藏好。这个思路在十几年前我第一次接触时,真是眼前一亮。
3.1 RSA 算法核心
RSA 的名字来自三位发明者 Rivest、Shamir、Adleman。它的安全性基于一个大整数分解的难度。
说白了就是:给你两个大素数 p 和 q 相乘得到 n 很容易,但给你 n 反推出 p 和 q,难得很。这个“单向门”就是 RSA 的根基。
3.1.1 密钥生成过程
我习惯把流程拆成四步,方便记忆:
- 选两个大素数 p、q(一般 1024 位以上)
- 计算 n = p × q,φ(n) = (p-1)(q-1)
- 选一个公钥指数 e(常用 65537),满足 1 < e < φ(n) 且 gcd(e, φ(n))=1
- 计算私钥 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。暴露配置项让用户选。另外签名验签一定要做公钥校验,防止恶意注入无效曲线造成的攻击。
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。我曾见过有人手写椭圆曲线点乘,结果常数时间没做,被侧信道攻击提取了私钥。