You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自研确定性AES加密代码问询:基于AesGcm的实现是否存在安全问题?

确定性加密实现的安全性问题分析

需求背景

需要对数据库数据进行加密,要求尽可能安全,同时支持相同数据对应固定密文(确定性加密)以便搜索。原本考虑AES-GCM-SIV/AES-SIV,但相关资源要么是归档项目要么是C库包装器,于是自行实现了基于AesGcm+Rfc2898DeriveBytes的版本,通过输入字符串生成确定性nonce。

自行实现的代码

internal class AesGcmStringEncryptor(IOptions<AppOptions> options)
{
    private readonly byte[] _key = Base32.FromBase32(options.Value.AesGcmBase32Key);
    private readonly byte[] _salt = Base32.FromBase32(options.Value.AesGcmBase32Salt);

    private const int TagSize = 16; // MaxSize = 16
    private const int NonceSize = 12; // MaxSize = 12

    public string Encrypt(string input)
    {
        // PBKDF2 pseudo random derivation function to build deterministic IVs.
        var ivGenerator = new Rfc2898DeriveBytes(input, _salt, 10000, HashAlgorithmName.SHA512);

        using var aes = new AesGcm(_key, TagSize);

        var nonceBytes = ivGenerator.GetBytes(NonceSize);

        var inputBytes = Encoding.UTF8.GetBytes(input);

        var cipherBytes = new byte[inputBytes.Length];

        var tagBytes = new byte[TagSize];

        aes.Encrypt(nonceBytes, inputBytes, cipherBytes, tagBytes);

        var combinedResult = Combine(nonceBytes, cipherBytes, tagBytes);

        return Base32.ToBase32(combinedResult);
    }

    public string Decrypt(string input)
    {
        using var aes = new AesGcm(_key, TagSize);

        var combinedResult = Base32.FromBase32(input);

        var dataLength = combinedResult.Length - NonceSize - TagSize;

        var resultBytes = new byte[dataLength];

        var nonceBytes = combinedResult[..NonceSize];
        var cipherBytes = combinedResult[NonceSize..^TagSize];
        var tagBytes = combinedResult[^TagSize..];

        aes.Decrypt(nonceBytes, cipherBytes, tagBytes, resultBytes);

        return Encoding.UTF8.GetString(resultBytes);
    }
}

代码存在的问题分析

  • AES-GCM的核心安全规则被打破:AES-GCM的安全基础是同一密钥下nonce绝对不能重复。你通过输入字符串生成固定nonce,意味着相同明文会重复使用同一个nonce。一旦攻击者触发重复加密(哪怕是已知明文场景),就能直接计算出GCM的认证密钥,进而伪造任意密文的认证标签,完全破坏加密的保密性和完整性。

  • PBKDF2的误用:PBKDF2是密钥派生函数,设计用于从用户密码生成加密密钥,而非生成nonce。用它生成nonce既浪费性能(10000次SHA512迭代),也没有额外的安全性增益——甚至不如直接用SHA-256哈希输入字符串生成nonce高效。

  • 未利用AES-GCM的额外认证数据(AAD):AES-GCM支持添加AAD来验证加密上下文的合法性(比如数据所属的表、字段),你的实现完全忽略了这一点,导致密文被误放到其他场景时,解密不会报错,可能引发数据混淆或伪造风险。

  • 确定性加密的固有风险:虽然是需求要求,但必须明确:这种模式会泄露明文的频率信息——攻击者可以通过统计密文出现次数,推断出明文的常见程度(比如高频密文对应"admin""user"这类常见值),这是确定性加密无法避免的trade-off。

改进建议

  • 放弃用AES-GCM做确定性加密:它的设计目标是随机性加密,重复nonce的风险不可接受。
  • 改用专门的确定性加密算法:优先选择AES-SIV,可寻找维护良好的.NET实现,避免使用归档或第三方包装库。
  • 若必须自行实现,可考虑基于抗碰撞哈希生成唯一nonce,但要严格处理哈希碰撞的可能性,同时结合AAD增强上下文认证。

内容的提问来源于stack exchange,提问作者dotnet-dave

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 15:12:42