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

.NET Framework中AES解密失败问题求助

AES加密用户名后解密结果为空的排查与解决

问题描述

尝试通过HTTP头向服务端发送AES加密的用户名(已知有更优实现方式),但解密后的字符串始终为空。

加密代码(客户端.NET 8)

using var aes = Aes.Create();
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.ECB;
aes.GenerateIV();
aes.Key = SHA256.HashData(Encoding.Unicode.GetBytes(PK));
var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

using MemoryStream memoryStream = new MemoryStream();
using CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
using StreamWriter streamWriter = new StreamWriter(cryptoStream, Encoding.Unicode);
string payload = UserProvider.User;

streamWriter.Write(payload);
cryptoStream.FlushFinalBlock();

byte[] array = memoryStream.ToArray();
string encryptedString = Convert.ToBase64String(array);
string ivString = Convert.ToBase64String(aes.IV);

_client.AddDefaultHeader("user", encryptedString);
_client.AddDefaultHeader("iv", ivString);

解密代码(服务端.NET Framework 4.8)

internal static class Authentication
{
    private const string PK = "MySecretKey";
    public static string DecryptUser(string encrypted, string iv)
    {

        using (var aes = Aes.Create())
        using (var sha = SHA256.Create())
        {
            byte[] pkBytes = Encoding.Unicode.GetBytes(PK);
            byte[] pkHash = sha.ComputeHash(pkBytes);
            byte[] ivBytes = Convert.FromBase64String(iv);
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.ECB;
            aes.Key = pkHash;
            aes.IV = ivBytes;
            var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

            byte[] cipher = Convert.FromBase64String(encrypted);
            using (MemoryStream memoryStream = new MemoryStream(cipher))
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
            using (StreamReader streamReader = new StreamReader(cryptoStream))
            {
                string user = streamReader.ReadToEnd();

                if (user.IsNullOrWhiteSpace()) throw new Exception("Authentication failed.");

                return user;
            }
        }
    }
}

已排查项

  • CipherMode配置正确
  • PaddingMode配置正确
  • 接收的Base64字符串非空且未损坏
  • 尝试过将MemoryStream定位到起始位置
  • 尝试发送更长内容,排除解密器最小块大小问题
  • 尝试将CryptoStream复制到另一个MemoryStream,但结果缓冲区为空
  • 执行ReadToEnd()后,cryptoStream.HasFlushedFinalBlock从false变为true

环境说明

  • 服务端(解密侧):.NET Framework 4.8
  • 客户端(加密侧):.NET 8

补充测试代码(xUnit,排除Base64/HTTP环节)

public class UnitTest1
{
    [Fact]
    public void TestMethod1()
    {
        byte[] buffer = new byte[1024];
        using (var aes = Aes.Create())
        using (var sha = SHA256.Create())
        {
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.ECB;
            aes.Key = sha.ComputeHash(Encoding.Unicode.GetBytes("Password"));

            using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
            using (MemoryStream memoryStream = new MemoryStream())
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
            using (StreamWriter streamWriter = new StreamWriter(cryptoStream, Encoding.Unicode))
            {
                string payload = "username";

                streamWriter.Write(payload);
                cryptoStream.FlushFinalBlock();

                buffer = memoryStream.ToArray();
                Assert.NotEmpty(buffer);
            }
            using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
            using (MemoryStream memoryStream = new MemoryStream(buffer))
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
            using (StreamReader streamReader = new StreamReader(cryptoStream, Encoding.Unicode))
            {
                string user = streamReader.ReadToEnd();

                Assert.True(streamReader.EndOfStream);
                Assert.True(cryptoStream.HasFlushedFinalBlock);
                Assert.Equal("username", user);
            }
        }
    }
}

问题根源与解决

核心问题1:ECB模式错误使用IV

AES的ECB模式为无IV模式,对每个块独立加密,不需要初始化向量。但当前代码中:

  • 加密侧生成并传递IV给加密器,还通过HTTP头发送IV
  • 解密侧接收并设置IV到Aes实例
    虽然ECB模式会忽略IV参数,但这种冗余操作可能引发状态不一致,同时增加不必要的复杂度。

核心问题2:加密与解密的编码不匹配

加密侧使用Encoding.Unicode(UTF-16LE)写入数据,但服务端解密侧的StreamReader未指定编码,默认使用UTF-8读取,导致解密后的UTF-16字节被错误解析,最终得到空字符串。

核心问题3:解密时数据读取不彻底

直接用StreamReader读取CryptoStream可能因内部缓冲机制,无法触发填充字节的自动移除,导致数据读取不完整。

修复步骤

1. 移除ECB模式下的IV相关代码

  • 加密侧:删除aes.GenerateIV(),将aes.CreateEncryptor(aes.Key, aes.IV)改为aes.CreateEncryptor(),不再发送iv请求头
  • 解密侧:删除IV参数接收与设置逻辑,将aes.CreateDecryptor(aes.Key, aes.IV)改为aes.CreateDecryptor()

2. 统一加密解密的编码

解密侧的StreamReader必须指定Encoding.Unicode,与加密侧保持一致。

3. 优化解密数据读取方式

先将CryptoStream的内容复制到MemoryStream,再用StreamReader读取,确保所有解密数据被处理,同时触发填充字节的自动移除。

修复后的测试代码示例

public class UnitTest1
{
    [Fact]
    public void TestMethod1()
    {
        byte[] buffer = new byte[1024];
        using (var aes = Aes.Create())
        using (var sha = SHA256.Create())
        {
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.ECB;
            aes.Key = sha.ComputeHash(Encoding.Unicode.GetBytes("Password"));

            // 加密部分
            using (var encryptor = aes.CreateEncryptor())
            using (MemoryStream memoryStream = new MemoryStream())
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
            using (StreamWriter streamWriter = new StreamWriter(cryptoStream, Encoding.Unicode))
            {
                string payload = "username";
                streamWriter.Write(payload);
                cryptoStream.FlushFinalBlock();
                buffer = memoryStream.ToArray();
                Assert.NotEmpty(buffer);
            }

            // 解密部分
            using (var decryptor = aes.CreateDecryptor())
            using (MemoryStream memoryStream = new MemoryStream(buffer))
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
            using (MemoryStream decryptedStream = new MemoryStream())
            {
                cryptoStream.CopyTo(decryptedStream);
                decryptedStream.Position = 0;
                using (StreamReader streamReader = new StreamReader(decryptedStream, Encoding.Unicode))
                {
                    string user = streamReader.ReadToEnd();
                    Assert.Equal("username", user);
                }
            }
        }
    }
}

修复后的服务端解密代码示例

internal static class Authentication
{
    private const string PK = "MySecretKey";
    public static string DecryptUser(string encrypted)
    {
        using (var aes = Aes.Create())
        using (var sha = SHA256.Create())
        {
            byte[] pkBytes = Encoding.Unicode.GetBytes(PK);
            byte[] pkHash = sha.ComputeHash(pkBytes);
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.ECB;
            aes.Key = pkHash;

            var decryptor = aes.CreateDecryptor();
            byte[] cipher = Convert.FromBase64String(encrypted);

            using (MemoryStream memoryStream = new MemoryStream(cipher))
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
            using (MemoryStream decryptedStream = new MemoryStream())
            {
                cryptoStream.CopyTo(decryptedStream);
                decryptedStream.Position = 0;
                using (StreamReader streamReader = new StreamReader(decryptedStream, Encoding.Unicode))
                {
                    string user = streamReader.ReadToEnd();
                    if (string.IsNullOrWhiteSpace(user)) throw new Exception("Authentication failed.");
                    return user;
                }
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 08:25:57