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

如何清除内存中AES密钥的所有副本?或阻止.NET运行时复制密钥

问题

我尝试使用AES进行解密,并显式清除密钥占用的内存。解密时传入密钥,完成后调用CryptographicOperations.ZeroMemory方法清零自己持有的密钥内存:

// 示例密钥,实际密钥是通过专用硬件上的非对称私钥解密得到的
byte[] key = System.Security.Cryptography.RandomNumberGenerator.GetBytes(32);

using (Aes aes = Aes.Create())
{
    aes.Key = key;

    // 在此处使用aes对象执行解密操作
}

CryptographicOperations.ZeroMemory(key);

但查看.NET运行时中Aes的Key属性实现,发现它通过CloneByteArray方法创建了密钥的副本:

public virtual byte[] Key
{
    get
    {
        if (KeyValue == null)
            GenerateKey();
        return KeyValue.CloneByteArray()!;
    }

    set
    {
        ArgumentNullException.ThrowIfNull(value);

        long bitLength = value.Length * 8L;
        if (bitLength > int.MaxValue || !ValidKeySize((int)bitLength))
            throw new CryptographicException(SR.Cryptography_InvalidKeySize);

        // 必须将字节转换为比特数
        this.KeySize = (int)bitLength;
        KeyValue = value.CloneByteArray();
    }
}

CloneByteArray扩展方法的实现如下:

public static byte[]? CloneByteArray(this byte[]? src)
{
    if (src == null)
    {
        return null;
    }

    return (byte[])(src.Clone());
}

请问是否有办法显式清除处理AES密钥的所有内存(包括.NET运行时创建的所有副本)?或者能否阻止.NET运行时复制密钥,仅使用我自行分配的密钥?

解决方案

1. 无法直接阻止.NET运行时复制密钥

标准Aes实现的Key属性设计逻辑就是克隆传入的密钥,目的是避免外部修改密钥导致加密/解密过程出现意外。因此没办法直接强制它使用你自行分配的密钥数组,必须接受副本的存在。

2. 清除.NET内部密钥副本的可行方案

方案一:使用AesCng(Windows平台专属)

如果代码运行在Windows环境下,可以使用AesCng类,它提供了对密钥内存更精细的控制:

byte[] key = System.Security.Cryptography.RandomNumberGenerator.GetBytes(32);

using (var aes = new AesCng())
{
    // 使用ImportKeyMaterial方法,可指定不保留密钥在内存中
    aes.ImportKeyMaterial(key, CngKeyBlobFormat.GenericBlob);
    
    // 执行解密操作
}

CryptographicOperations.ZeroMemory(key);

AesCng依赖Windows CNG加密库,导入密钥时可通过参数控制密钥存储方式,部分场景下能减少内存中的密钥副本;且当AesCng实例被释放时,内部的密钥内存会被CNG库自动清零。

方案二:自定义Aes实现(跨平台)

如果需要跨平台支持,可以基于SymmetricAlgorithm抽象类实现自己的Aes逻辑,直接使用传入的密钥数组而不克隆。但这种方式需要自行处理密钥验证、加密/解密核心逻辑,复杂度较高,且要确保内存操作的安全性。

方案三:利用GC和内存压力(兜底方案,不推荐)

虽然无法直接访问.NET内部的KeyValue数组,但可以在使用完Aes实例后,强制触发垃圾回收,配合CryptographicOperations.ZeroMemory处理托管内存:

using (Aes aes = Aes.Create())
{
    aes.Key = key;
    // 执行解密操作
}

CryptographicOperations.ZeroMemory(key);
// 提示GC回收Aes实例
GC.Collect();
GC.WaitForPendingFinalizers();

但这种方式不可靠,因为GC的回收时机不确定,无法保证内部密钥副本一定会被清零,仅能作为兜底手段。

3. 额外注意事项

  • 避免调用Aes.Key的getter方法,每次调用都会创建新的密钥副本,增加内存中密钥的残留风险。
  • 对于敏感密钥,尽量减少其在内存中的停留时间,用完立即清零自己持有的密钥数组。

内容的提问来源于stack exchange,提问作者johanrex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 02:49:59