数字信封必须用混合加密:RSA封装AES密钥(推荐OAEP填充)+AES-GCM加密数据,因RSA直接加密受限(如2048位仅190字节),AES又无法安全分发密钥。

crypto/rsa 和 crypto/aes 配合使用就能实现数字信封(即混合加密),不需要第三方库。Go 标准库已完全支持,关键在于密钥封装逻辑和 AEAD 模式的选择。
数字信封为什么必须用混合加密
纯 RSA 加密明文受限严重:2048 位密钥最多只能加密 256 - 2*32 - 2 = 190 字节(OAEP 填充下),远不够加密实际数据;而 AES 又无法安全分发密钥。数字信封本质是「用 RSA 封装 AES 密钥 + 用 AES 加密数据」的两层结构。
常见错误现象:
- 直接用
rsa.EncryptPKCS1v15加密大段文本 → 报错crypto/rsa: message too long - 手动生成 AES 密钥后硬编码进代码 → 导致密钥泄露风险
- 用 CBC 模式但忽略填充或 IV 复用 → 解密失败或被篡改无感知
用 rsa.EncryptOAEP 封装 AES 密钥
OAEP 是目前推荐的填充方案,比 PKCS#1 v1.5 更抗选择密文攻击。封装时注意三点:
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
label参数建议设为空切片([]byte{}),除非业务有明确上下文绑定需求 - 哈希函数必须与解密端一致,通常用
sha256.New() - 封装后的密钥长度固定为 RSA 密钥字节数(如 2048 位 → 256 字节),可用于后续 AES 初始化
示例片段:
block, _ := aes.NewCipher(aesKey) gcm, _ := cipher.NewGCM(block) nonce := make([]byte, gcm.NonceSize()) rand.Read(nonce) ciphertext := gcm.Seal(nil, nonce, plaintext, nil) <p>// 封装 aesKey(32 字节)用公钥 encryptedKey, _ := rsa.EncryptOAEP(sha256.New(), rand.Reader, &pubKey, aesKey, nil)
优先选 cipher.NewGCM 而非 NewCBCEncrypter
GCM 是 AEAD(Authenticated Encryption with Associated Data)模式,一次调用同时完成加密+认证,避免手动处理填充、MAC 校验等易错环节。CBC 则需额外做 PKCS#7 填充 + 单独 HMAC 校验,出错率高且性能差。
参数差异要点:
-
gcm.NonceSize()返回 12(GCM 标准),不能随意改;CBC 的 IV 长度等于aes.BlockSize(16) - GCM 的
Seal第四个参数是additionalData,可用于绑定文件名、时间戳等元信息(不加密但参与认证) - GCM 认证标签默认 16 字节,附加在密文末尾,解密时
Open会自动校验
解密时顺序不能颠倒:先开信封,再解密数据
典型错误是把封装密钥和密文一起传给 rsa.DecryptOAEP,或者用错私钥类型(比如传了 *rsa.PublicKey)。正确流程:
- 用
rsa.DecryptOAEP解封得到原始aesKey []byte - 用该 key 初始化
aes.Block,再构造cipher.AEAD - 调用
aead.Open,传入完整密文(含 nonce + ciphertext + tag),并指定相同additionalData
容易忽略的一点:nonce 必须从密文开头按 gcm.NonceSize() 截取,不能靠约定长度硬写死 —— 不同 AEAD 实现 nonce 长度可能不同(如 ChaCha20-Poly1305 是 12,AES-GCM 也是 12,但自定义实现可能不同)。

















