常见加密算法与模式:AES-GCM、RSA-OAEP、国密SM4

做字段级加密,首先得选对算法。这不是一个简单的技术决定,它直接影响兼容性、性能和安全边界。我个人习惯把算法分为两类:对称加密和公钥加密。前者解决数据量大、性能敏感的加解密需求,后者解决密钥分发和身份认证。今天重点讲三个最常用的组合:AES-GCM、RSA-OAEP 和国密 SM4。

你可能会问,为什么偏偏是它们?说白了,它们都是经过实践检验的「正规军」,在 TEE、HSM、云原生和国密合规场景里出现频率最高。

AES-GCM:对称加密的「默认选项」

AES-GCM 是 AES 加密算法在 Galois/Counter Mode(GCM)下的工作模式。它同时提供机密性和完整性校验,不需要再单独加 HMAC。这一点在实际工程中非常省心。

特性 说明
算法基础 AES(128/192/256-bit)
工作模式 GCM(Galois/Counter Mode)
输出结构 密文 + 认证标签(通常 16 字节)
初始化向量(IV) 推荐 12 字节(96-bit),随机生成
性能特征 硬件加速支持下极快,适合大量数据

我在项目中遇到过一个问题:团队刚开始用 AES-GCM 时,把 IV 设置成了固定值。结果在测试环境里发现,同样明文加密后密文完全一样。这是大忌!GCM 的 IV 必须随机生成,否则多个密文之间可以推导出密钥流。

重要提醒:AES-GCM 的 IV 不要重复使用。同一个密钥下,哪怕 IV 重复一次,攻破可能性就急剧上升。我建议每次加密前调用安全随机数生成器产生 IV,并和密文一起存储。

AES-GCM 的兼容性非常好。主流编程语言的加密库都原生支持:OpenSSL、BoringSSL、Java JCE(AES/GCM/NoPadding)、Go 标准库 crypto/aes + crypto/cipher。如果你的 SDK 面向全球市场,AES-GCM 是最稳妥的选择。

// 示例:Go 语言下的 AES-256-GCM 加密
import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
)

func encrypt(plaintext []byte, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }
    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return nil, err
    }
    nonce := make([]byte, gcm.NonceSize())
    if _, err := rand.Read(nonce); err != nil {
        return nil, err
    }
    return gcm.Seal(nonce, nonce, plaintext, nil), nil
}

RSA-OAEP:公钥加密的黄金标准

对称加密解决了密钥难管理的问题吗?没有。你需要安全地传递 AES 密钥。「那我用 RSA 加密 AES 密钥」——这个思路就是 RSA-OAEP 的典型应用场景。

RSA 本身有几种填充模式:PKCS#1 v1.5 和 OAEP。PKCS#1 v1.5 曾经是主流,但现在被认为是过时且不安全的。OAEP(Optimal Asymmetric Encryption Padding)是目前 RSA 加密的首选。它引入了随机化填充,每次加密同一个明文会产生不同的密文,有效防止选择明文攻击(CPA)。

我记得早期做过一个兼容性测试:A 系统用 Java 的 Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding") 加密,B 系统用 Python 的 from Crypto.Cipher import PKCS1_OAEP 解密。刚开始死活对不上,排查半天发现是 SHA 算法不一致。这里埋个避坑点——

个人经验:RSA-OAEP 的参数匹配必须在 SDK 级别严格控制。包括:
  • 哈希算法(如 SHA-256)
  • MGF1 的哈希算法(通常和主哈希一致)
  • 标签(Label,通常为空)
只要一个参数不匹配,跨库解密就会失败。

RSA-OAEP 的主要场景是「密钥封装」,不是直接加密大数据。数据量一大,RSA 的性能就撑不住了。实践中我通常用它加密 32 字节的 AES 密钥,然后和 AES-GCM 加密过的数据一起传输。

国密 SM4:自主可控的选择

SM4 是中国国家密码管理局公布的商用密码分组算法。它的分组长度 128 位,密钥长度也是 128 位,本质上是一个 32 轮的非平衡 Feistel 结构。很多人拿它和 AES 对标,说它是中国的 AES。这个说法有一定道理,因为两者在功能上高度重叠:都是对称加密,都支持 ECB/CBC/CTR/GCM 等模式。

不过在实际测试中,我发现 SM4 在软件实现上的性能大约比 AES 慢 20%-30%。这是算法设计带来的差异。但 SM4 有独特的优势:

  • 合规性:在金融、政务、运营商等领域,国家政策要求必须使用国密算法。
  • 自主可控:不依赖国外密码算法,防止后门风险。
  • 生态逐渐成熟:OpenSSL 1.1.1+、BoringSSL、腾讯的 TencentSM、各家 HSM 都开始支持 SM4。
关键点:SM4 本身只定义了分组加密算法,没有强制使用哪种模式。字段级加密场景下,我强烈建议使用 SM4-GCM(国密局已发布 GM/T 0102-2020 标准)。它和 AES-GCM 一样提供认证加密,兼容性优于 CBC 模式。

算法选择依据:一个决策框架

讲完了每种算法的基本特性,怎么选?我总结了一个简单的决策框架,帮你快速确定当前项目该用哪个。

我在多个 SDK 项目中用过这个框架,没出过大问题。你可以把它贴在墙上当对照表。
场景 推荐算法 原因
通用云环境/国际化 AES-GCM-256 性能好、生态成熟、安全强度高
密钥分发/小数据加密 RSA-OAEP(SHA-256) 公钥加密,无需共享对称密钥
国密合规项目 SM4-GCM 必须严格遵循 GM/T 标准
混合架构(国密+国际) AES-GCM + SM4-GCM 双算法兼容,切换灵活

以前有个客户要求既要支持国密,又要和国际系统互通。我当时的设计方案是:在 SDK 内部支持两套算法引擎,通过配置文件切换。数据在存储时统一用 SM4-GCM,传输到国际端时可以通过网关转成 AES-GCM。这个方案虽然增加了一点测试量,但保证了兼容性。

这里是算法选择时的简单逻辑流程——

算法选择决策流程 开始选择算法 是否要求国密合规? 是 → SM4-GCM 否 → 场景判断 数据量是否大于 1KB? 是 → RSA-OAEP 密钥封装 否 → AES-GCM-256 注:此流程适用于字段级加密场景,整体数据加密需考虑性能与密钥管理

常见误区与避坑指南

讲几个我自己踩过的坑,希望能帮你省点时间。

坑一:AES-GCM 的 Tag 长度不一致。
不同库默认的 Tag 长度不同。有的用 12 字节,有的用 16 字节。跨 SDK 解密时如果不显式指定,就会失败。我建议统一约定为 16 字节(128 位),安全性最高。
坑二:RSA 密钥对序列化格式错误。
Java 默认用 DER 编码,Python 常用 PEM。做兼容测试时,记得在 SDK 里强制定为一种标准格式(比如 PKCS#8 DER),或者提供转换工具。
个人经验:SM4 的密钥长度固定 128 位,而 AES 支持 128/192/256。如果同时使用这两种算法,密钥管理上要区分版本号,避免混用导致错误。我通常会在加密数据包头部写一个 1 字节的算法版本标识,这样解析时就能自动选择对应的解密逻辑。

最后补充一句:算法选择不是「一劳永逸」的事。随着量子计算和密码分析的发展,今天安全的算法明天可能就不安全了。作为 SDK 工程师,留好算法扩展接口,允许未来无缝切换——这才是架构级的思考。