4. 常见非对称加密算法:RSA、ECC原理及SDK实现

聊完对称加密,咱们来看看非对称加密。它解决了一个核心问题——密钥分发。对称加密里,通信双方得事先约定同一个密钥,这在实际场景中非常麻烦。非对称加密则用一对密钥:公钥公开,私钥保密。公钥加密的数据只能用私钥解密,反过来私钥签名可以用公钥验签。这套机制撑起了现代互联网的安全基石。

非对称加密速度慢,通常只用来加密少量数据(比如对称密钥)或做数字签名。在数据防泄漏场景中,我们用非对称加密来保护密钥交换环节,避免密钥在传输中被窃取。另外,先哈希再签名也能确保数据的完整性和来源可信。

下面针对两种主流算法——RSA 和 ECC,我结合项目经验讲讲它们的原理和 SDK 实现要点。

4.1 RSA 算法:大数分解难题

RSA 是应用最广泛的非对称加密算法,依赖大整数分解的数学难题。简单说,两个大质数相乘很容易,但从乘积反推出这两个质数却极其困难。RSA 的安全性就建立在这个不对称性上。

密钥生成过程:选两个大质数 p 和 q,计算 n = p * q,再找 e 和 d 使得 e*d ≡ 1 (mod φ(n)),其中 φ(n) = (p-1)*(q-1)。公钥是 (n, e),私钥是 (n, d)。加密时计算 c = m^e mod n,解密时 m = c^d mod n。签名过程类似,只是用私钥加密,公钥验证。

RSA 的密钥长度通常为 2048 位或 4096 位。位数越长越安全,但加解密速度也越慢。2048 位是目前推荐的最低安全标准,4096 位用于高安全场景。

密钥长度安全级别性能表现推荐场景
1024 位已不安全较快不再使用
2048 位中等(~112 位对称安全)可接受常规业务
4096 位高(~128 位对称安全)较慢金融、政务

SDK 实现示例(Java):

// 初始化密钥对生成器
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);  // 我习惯用2048位
KeyPair keyPair = keyGen.generateKeyPair();

PublicKey pubKey = keyPair.getPublic();
PrivateKey privKey = keyPair.getPrivate();

// 加密数据(RSA加密原数据有长度限制,2048位最多加密245字节)
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted = cipher.doFinal(data);

// 解密数据
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] decrypted = cipher.doFinal(encrypted);

// 签名
Signature signer = Signature.getInstance("SHA256WithRSA");
signer.initSign(privKey);
signer.update(data);
byte[] signature = signer.sign();

// 验签
signer.initVerify(pubKey);
signer.update(data);
boolean verified = signer.verify(signature);
经验之谈: 做 RSA 加密时千万别用无填充模式(ECB/NOPADDING),那是赤裸裸的漏洞。OAEP 填充是行业标准,能抵抗选择密文攻击。我记得有次代码审查,发现同事用 PKCS1Padding,我直接建议改成 OAEPWithSHA-256。

4.2 ECC 算法:椭圆曲线离散对数难题

ECC 是新一代非对称加密算法,基于椭圆曲线离散对数难题。它的核心优势是短密钥、高强度。256 位的 ECC 密钥就能提供和 3072 位 RSA 相当的安全性,而且计算效率更高。

ECC 的数学原理相对复杂:椭圆曲线上的点构成一个阿贝尔群,定义点加法和标量乘法。私钥是一个随机整数,公钥是私钥乘以生成元 G 得到的点。已知公钥求私钥,就是离散对数问题,目前没有多项式时间解法。

ECC vs RSA 对比:

对比维度RSAECC
安全性基础大整数分解椭圆曲线离散对数
密钥长度(同安全性)3072 位256 位
加解密速度慢(尤其是私钥操作)快(私钥操作优势明显)
签名速度签名慢、验签快签名快、验签较快
密钥生成速度慢(找大质数)快
带宽占用大(密钥、签名长度大)小(密钥、签名长度短)

为什么 ECC 性能更优?我没法给你讲透所有数学细节,但说个直观感受:ECC 用 256 位密钥能达到 AES-128 的安全级别,而 RSA 需要 3072 位。位数少,计算量自然小。这在移动端和 IoT 设备上尤其重要。

SDK 实现示例(Java):

// 生成ECC密钥对
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
keyGen.initialize(new ECGenParameterSpec("secp256r1"));  // 我用这条曲线,兼容性好
KeyPair keyPair = keyGen.generateKeyPair();

PublicKey pubKey = keyPair.getPublic();
PrivateKey privKey = keyPair.getPrivate();

// ECC不直接用于加解密(复杂,且性能不如对称加密)
// 通常用于数字签名和密钥交换

// ECDSA签名
Signature signer = Signature.getInstance("SHA256withECDSA");
signer.initSign(privKey);
signer.update(data);
byte[] signature = signer.sign();

// 验签
signer.initVerify(pubKey);
signer.update(data);
boolean ok = signer.verify(signature);

// ECDH密钥交换(双方交换公钥后,各自计算共享密钥)
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
keyAgreement.init(privKey);
keyAgreement.doPhase(peerPublicKey, true);
byte[] sharedSecret = keyAgreement.generateSecret(); // 这就是对称密钥
核心实践: 在防泄漏 SDK 中,我通常这样搭配:用 ECDH 做密钥协商,用对称加密算法加密实际数据,用 ECDSA 做签名确保完整性。这套组合在性能和安全上都有保障。

4.3 算法选型与避坑指南

选择 RSA 还是 ECC?其实没有答案。我结合项目经验给出建议:

  • 新项目优先选 ECC:密钥短、性能好、安全性高。除非要兼容老旧系统。
  • 遗留系统继续用 RSA:改动成本高,且 RSA 在 2048 位以上仍足够安全。
  • 移动端/IoT 必须考虑 ECC:带宽和算力有限,ECC 优势明显。
  • 高安全场景用 4096 位 RSA 或 384 位 ECC:但要评估性能影响。
我曾经踩过的坑: 有次做 SDK,选了 secp256r1 曲线,一切正常。后来接入某银行系统,发现对方只支持 secp384r1。结果不得不改代码、重新测试。所以,选曲线时一定要确认上下游支持的曲线列表,别想当然。目前 secp256r1 和 secp384r1 是主流,secp521r1 用得少但更安全。

另外,不要自己实现加密算法!这是铁律。用标准库(Java JCA、OpenSSL、Bouncy Castle 等)和公认的算法标识。你想想看,自己写个 RSA 实现,万一哪步错了,密钥就可能被破解。我见过有人用自制 RSA,结果因为随机数生成器伪随机导致密钥可预测……嗯,那项目后来重写了。

非对称加密算法对比与选型 RSA 算法 大数分解难题 2048/4096 位 加解密 + 签名 对比 ECC 算法 椭圆曲线离散对数 256/384 位 签名 + 密钥交换 安全性等效 选型建议 • 新项目:优先 ECC • 遗留系统:继续 RSA • 移动端/IoT:必选 ECC • 高安全:4096 RSA 或 384 ECC • 密钥交换:首选 ECDH • 签名:ECDSA 性能佳 选择曲线/密钥长度 生成密钥对 数字签名/验签 密钥交换(ECDH)生成对称密钥

4.4 安全防御与 SDK 实现要点

光会用 API 还不够,还得防攻击。非对称加密有几个常见风险:

  1. 侧信道攻击:攻击者通过时间、功耗、电磁泄漏等物理特征推断密钥。实现时必须用常数时间算法,避免密钥相关分支。
  2. 填充预言攻击:攻击者观察解密是否报错来猜测明文。用 OAEP 填充并统一报错信息可以防御。
  3. 密钥泄露:私钥必须严格保护,最好用硬件安全模块(HSM)或安全隔区存储。
  4. 重放攻击:签名数据中加入时间戳或随机数,确保唯一性。
SDK 实现小技巧: 解析密钥时,我习惯先用 ASN.1 结构校验密钥格式,防止畸形输入。另外,一定要设置密钥过期时间,定期轮换。曾经有个客户私钥用了 5 年没换,虽然没出事,但听着就悬。