C# AES 加密行为异常

C# AES encryption behaving strangely

我正在尝试使用这段代码在 C# 中加密字符串:

public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
     // Create an Aes object with the specified key and IV
     using Aes aesAlg = Aes.Create();
     aesAlg.Key = Key;
     aesAlg.IV = IV;

     // Create an encryptor to perform the stream transform
     ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

     // Create the streams used for encryption
     using MemoryStream msEncrypt = new MemoryStream();
     using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
     using StreamWriter swEncrypt = new StreamWriter(csEncrypt);

     // Write all data to the stream
     swEncrypt.Write(plainText);

     return Encoding.UTF8.GetString(msEncrypt.ToArray());
}

这主要基于此处找到的样本:example

然而,结果有点 st运行ge 和不可预测 - 一次我 运行 代码并得到一些字符串作为结果,另一次结果字符串(和 msEncrypt 流) 是空的,有一次应用程序甚至冻结了。我不明白这是什么问题。

编辑: 我只是再次 运行 代码,在这一行:

 aesAlg.Key = Key;

发生了一些事情运行ge,我得到了这个错误:

下面是我实际调用此方法的方式:

public static class Authentication
{
        ...

        private static readonly AesEncryption aes = new AesEncryption();

        public static string GenerateToken()
        {
            AuthenticationData data = new AuthenticationData()
            {
                // some data
            };

            string serialized = JsonConvert.SerializeObject(data);
            return AesEncryption.AesEncrypt(serialized, aes.Key, aes.IV);
        }
}

可能每次调用AesEncrypt方法时传入的key和IV都不一样。 对我来说,这个例子很好用。

static void Main(string[] args)
    {

        Aes aesAlg = Aes.Create();
        AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
        AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
        AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
        AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
        AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);

        Console.ReadKey();
    }

    public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
    {
        // Create an Aes object with the specified key and IV
        using (Aes aesAlg = Aes.Create())
        {
            aesAlg.Key = Key;
            aesAlg.IV = IV;

            // Create an encryptor to perform the stream transform
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

            // Create the streams used for encryption
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        swEncrypt.Write(plainText);

                    }

                    Console.WriteLine(BitConverter.ToString(msEncrypt.ToArray()));
                    return Encoding.UTF8.GetString(msEncrypt.ToArray());

                }

            }

        }


    }

缓冲区和刷新流可能有问题。

尝试在 msEncript.ToArray()

之前仅 swEncript 使用范围
  public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
    {
         // Create an Aes object with the specified key and IV
         using Aes aesAlg = Aes.Create();
         aesAlg.Key = Key;
         aesAlg.IV = IV;

         // Create an encryptor to perform the stream transform
         ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

         // Create the streams used for encryption
         using MemoryStream msEncrypt = new MemoryStream();
         using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
         using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) {
             // Write all data to the stream
             swEncrypt.Write(plainText);
         }

         return Encoding.UTF8.GetString(msEncrypt.ToArray());

    }

这段代码有两个问题。

  1. SteamWriter 在使用流之前未关闭。也没有调用 StreamWriter.Flush。 StreamWriter 在内部使用缓冲区并将其写出到 流满时。

    StreamWriter 不知道它正在写入 一个 MemoryStream,所有的流对它来说都是一样的。鉴于有多小 在大多数示例中都是明文,在尝试之前不会发生刷新 从内存流中读取数据。

  2. UTF8 仅支持一组有限的字节序列。好的加密算法虽然会生成基本上随机的序列。其中一些不会对应于任何有效的 UTF8 序列。将二进制数据编码为文本的最常见方法之一是使用 Base64 编码。这就是大多数加密或散列样本所显示的内容。

代码应更改为:

public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
     // Create an Aes object with the specified key and IV
     using Aes aesAlg = Aes.Create();
     aesAlg.Key = Key;
     aesAlg.IV = IV;

     ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

     using MemoryStream msEncrypt = new MemoryStream();
     using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
     using StreamWriter swEncrypt = new StreamWriter(csEncrypt);
     swEncrypt.Write(plainText);

     //CHANGES HERE
     swEncrypt.Flush();
     return Convert.ToBase64String(msEncrypt.ToArray());
}

示例没有遇到此问题,因为它使用显式 using 块,因此在使用内存流 之前 关闭编写器:

using (MemoryStream msEncrypt = new MemoryStream())
{
    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
    {
        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
        {
            swEncrypt.Write(plainText);
        }
        encrypted = msEncrypt.ToArray();
    }
}

Visual Studio 和 Resharper 都不建议在这种情况下使用 using 语句,因为 writer 显然在最里面的块中关闭。

不要混用 using 样式

您可以混合使用块和语句,例如:

using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
    swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();

考虑到已经造成的混乱,您可能不应该这样做。代码并没有那么清晰,它已经引起了问题。将来看到相同代码的其他人也可能会感到困惑,并使用糟糕的重构