4. 非对称加密API实战

非对称加密是密码API开发中使用频率很高、也最容易踩坑的一类能力。与对称加密“一把钥匙加解密”不同,非对称加密使用一对数学上关联的密钥:公钥可以公开,用于加密或验签;私钥必须严格保密,用于解密或签名。本节围绕RSA与SM2两种主流算法,讲清楚密钥对如何生成、加密解密API如何正确调用,以及在高并发场景下如何做性能优化。

一、先分清两种算法的定位差异

RSA基于大整数分解难题,是国际上最成熟的非对称算法,密钥长度常见2048位和4096位,兼容性极好,几乎所有语言和硬件都支持。SM2是我国商用密码标准(GM/T 0003)中定义的椭圆曲线公钥算法,基于椭圆曲线离散对数难题,256位密钥即可达到RSA 3072位左右的安全强度,密钥更短、运算更快,但需要依赖支持国密的密码库。

两者在API使用上有一个关键共性:非对称加密只适合加密短数据。RSA 2048位单次最多加密约245字节(PKCS#1 v1.5填充)或190字节(OAEP填充),SM2单次加密的数据长度更短。因此实际工程中,非对称加密几乎不直接加密业务数据,而是用来加密对称密钥,再由对称密钥加密大数据,这就是“数字信封”模式。

数字信封:非对称加密 + 对称加密的协作流程 发送方 ① 生成随机对称密钥 K (如 AES-256 / SM4) ② 用 K 加密业务数据 得到密文 C_data ③ 用接收方公钥加密 K 得到密文 C_key ④ 发送 C_data + C_key (可附加签名) 网络传输 接收方 ⑤ 用私钥解密 C_key 还原出对称密钥 K ⑥ 用 K 解密 C_data 还原业务明文 ⑦ 验签(可选) 确认数据来源与完整性 ⑧ 得到可信明文 完成安全通信 密钥对生成 RSA:2048 / 4096 位 SM2:256 位椭圆曲线 公钥公开 · 私钥保密 性能优化要点 密钥对缓存复用 会话密钥一次一密 SM2 替代 RSA 提速 硬件加速 / 异步调用 核心原则:非对称加密只加密对称密钥(短数据),业务数据由对称加密处理,兼顾安全与性能

二、密钥对生成:参数选择与代码示例

生成密钥对时,RSA需要指定密钥长度和填充模式,SM2需要指定椭圆曲线参数(国密推荐sm2p256v1)。以下以Java(BouncyCastle + JDK)为例,展示两种算法的密钥对生成过程。

// ===== RSA 2048 密钥对生成 =====
KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA");
rsaGen.initialize(2048, new SecureRandom());
KeyPair rsaKeyPair = rsaGen.generateKeyPair();
PublicKey rsaPub = rsaKeyPair.getPublic();
PrivateKey rsaPriv = rsaKeyPair.getPrivate();

// ===== SM2 密钥对生成(BouncyCastle) =====
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator sm2Gen = KeyPairGenerator.getInstance("EC", "BC");
sm2Gen.initialize(new ECGenParameterSpec("sm2p256v1"), new SecureRandom());
KeyPair sm2KeyPair = sm2Gen.generateKeyPair();

这里有一个容易被忽略的细节:SecureRandom必须使用强随机源。如果使用普通Random或固定种子,生成的密钥对可能被预测,直接导致私钥泄露。生产环境中建议显式指定new SecureRandom(),并确保操作系统熵池充足。

三、加密解密API调用:填充模式决定成败

RSA加密时,填充模式的选择直接影响安全性和兼容性。PKCS#1 v1.5填充虽然兼容性好,但存在Bleichenbacher攻击风险;OAEP填充更安全,但需要双方都支持。推荐新系统统一使用RSA/ECB/OAEPWithSHA-256AndMGF1Padding。

// RSA-OAEP 加密
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
rsaCipher.init(Cipher.ENCRYPT_MODE, rsaPub);
byte[] cipherText = rsaCipher.doFinal(plainBytes);

// RSA-OAEP 解密
rsaCipher.init(Cipher.DECRYPT_MODE, rsaPriv);
byte[] plainBytes = rsaCipher.doFinal(cipherText);

// SM2 加密(BouncyCastle)
Cipher sm2Cipher = Cipher.getInstance("SM2", "BC");
sm2Cipher.init(Cipher.ENCRYPT_MODE, sm2Pub);
byte[] sm2CipherText = sm2Cipher.doFinal(plainBytes);

SM2的API调用相对简洁,但要注意:SM2加密结果默认包含C1(椭圆曲线点)、C2(密文)、C3(摘要)三部分,不同库的拼接顺序可能不同(国密标准为C1||C3||C2),跨系统对接时必须确认格式,否则解密会失败。

四、性能优化策略:从算法选型到工程实践

非对称加密的性能瓶颈主要在密钥生成和大数运算。以RSA 2048为例,一次密钥生成在普通服务器上约需50~200毫秒,而一次加密约1~5毫秒、解密约5~20毫秒。SM2的密钥生成和加解密速度通常比RSA快一个数量级。针对高并发场景,可以从以下几个层面优化:

  • 密钥对缓存复用:服务端与固定客户端通信时,密钥对不必每次请求都重新生成,可缓存到内存或密钥管理服务中,设置合理的轮换周期(如7天)。
  • 会话密钥一次一密:每次会话生成新的对称密钥,用非对称公钥加密传输,既保证前向安全性,又避免非对称加密成为性能瓶颈。
  • 算法选型:在支持国密的环境下优先使用SM2,256位密钥即可达到RSA 3072位的安全强度,且运算更快、密钥更短。
  • 异步与连接池:将非对称加解密操作放入独立线程池,避免阻塞主业务线程;对Cipher对象进行池化管理,减少重复初始化开销。
  • 硬件加速:部分服务器支持AES-NI和国密硬件加速卡,可显著提升对称加密和SM2运算速度。
实践建议:在压测环境中对比RSA 2048与SM2 256的QPS和P99延迟。示例场景下,若RSA解密P99为18ms,SM2通常可降至3~5ms。但具体数值受硬件、库版本、并发度影响,应以实际压测结果为准,不要直接套用。

五、常见错误与排查方向

实际开发中,非对称加密报错往往集中在几类:javax.crypto.BadPaddingException通常意味着填充模式不匹配或密钥错误;InvalidKeyException可能是密钥格式(PKCS#8 vs X.509)解析错误;SM2解密失败则要检查C1C3C2与C1C2C3的顺序差异。排查时先确认双方算法、填充、密钥格式、密文拼接顺序四项是否完全一致,再检查密钥是否被截断或编码错误。

掌握以上内容后,RSA与SM2的密钥生成、加解密调用和性能优化就有了完整的落地路径。核心记住三点:非对称加密只加密短数据、填充模式必须双方一致、性能优化优先考虑会话密钥与算法选型。