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

.NET移植中AesManaged与Aes.Create()加密行为差异原因咨询

.NET Framework到.NET移植中AES加密行为差异问题解析

问题描述

将应用从.NET Framework移植到.NET后,第三方库中的AES加密代码出现行为不一致:

原加密代码如下:

public static byte[] AesEncrypt(byte[] key, byte[] iv, byte[] data)
{
    byte[] encrypted;
    using (AesManaged aes = new AesManaged())
    {
        aes.Key = key;
        aes.IV = iv;
        aes.BlockSize = 128;
        ICryptoTransform aesEnc = aes.CreateEncryptor();

        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, aesEnc, CryptoStreamMode.Write))
            {
                csEncrypt.Write(data, 0, data.Length);
                encrypted = msEncrypt.ToArray();
            }
        }
    }
    return encrypted;
}

public static void Main()
{
    var key = new byte[] { 78, 47, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    var iv = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    var data = new byte[] { 78, 47, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    var result = AesEncrypt(key, iv, data);
}
  • 在.NET Framework中,result始终返回固定值:
    89, 156, 29, 52, 98, 240, 113, 210, 16, 211, 121, 61, 53, 174, 12, 229
    
  • 在.NET中,返回结果完全不同且每次随机。

进一步测试发现:在.NET Framework中用Aes aes = Aes.Create()替代AesManaged,行为与.NET一致。

原因分析

  1. AesManaged的实现变更
    .NET Framework中的AesManaged是纯托管的AES实现;而在.NET(Core/.NET 5+)中,AesManaged已被标记为过时,内部会回退调用Aes.Create(),使用平台提供的硬件加速实现(如Windows的CNG、Linux/macOS的OpenSSL)。

  2. 核心错误:未完成加密流的最终处理
    代码的关键问题是未调用CryptoStream.FlushFinalBlock(),也未等待CryptoStream完成Dispose就读取内存流:

    • 在.NET Framework的AesManaged实现中,CryptoStream的Dispose逻辑可能会自动处理最后一块的填充操作,因此即使提前读取内存流,也能得到完整的加密结果。
    • 在.NET的平台原生AES实现中,CryptoStream不会自动完成未终止的加密操作。未调用FlushFinalBlock()会导致加密数据不完整,加上底层缓冲区的状态差异,最终输出随机或不完整的结果。
  3. 默认参数的隐式差异
    虽然AesManaged和Aes.Create()的默认模式(CBC)、填充(PKCS7)一致,但平台实现的细节差异,结合未正确终止加密流的操作,放大了行为不一致的问题。

修复方案

修改代码,在读取内存流前显式调用FlushFinalBlock()完成加密操作,同时推荐使用Aes.Create()替代过时的AesManaged,并显式指定加密模式和填充模式以消除默认值差异:

public static byte[] AesEncrypt(byte[] key, byte[] iv, byte[] data)
{
    byte[] encrypted;
    using (Aes aes = Aes.Create())
    {
        aes.Key = key;
        aes.IV = iv;
        aes.BlockSize = 128;
        aes.Mode = CipherMode.CBC; // 显式指定加密模式
        aes.Padding = PaddingMode.PKCS7; // 显式指定填充模式

        ICryptoTransform aesEnc = aes.CreateEncryptor();

        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, aesEnc, CryptoStreamMode.Write))
            {
                csEncrypt.Write(data, 0, data.Length);
                csEncrypt.FlushFinalBlock(); // 完成加密,处理最后一块填充
                encrypted = msEncrypt.ToArray();
            }
        }
    }
    return encrypted;
}

最佳实践

  • 永远显式指定加密算法的模式、填充方式,避免依赖不同平台或版本的默认值。
  • 避免使用已过时的AesManaged,改用Aes.Create()获取平台最优的加密实现。
  • 使用CryptoStream时,必须调用FlushFinalBlock()或等待其Dispose完成后,再读取底层流的数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 18:53:12