【问题标题】:Aes decryptor gives empty stringAes 解密器给出空字符串
【发布时间】:2013-03-01 07:59:56
【问题描述】:

我对 AES 加密/解密有疑问。注释代码有效,但有时会出现错误“填充无效且无法删除”,因此我更改了它,因为这里解释了 Padding is invalid and cannot be removed Exception while decrypting string using "AesManaged" C#

但是当我尝试解密时,下面的代码给出了一个空字符串。我不知道我在哪里犯错。两个静态函数 bytesToString 和 stringToBytes 与加密无关,我在其他地方使用它们。密钥长度和块大小是可以的。 我在调试器中找到了这个:

"'csEncrypt.Length' 引发了类型为 'System.NotSupportedException' 的异常"

我在 3.5 .NET Visual STudio 2008 上工作

这是来自调试器的 prtscr,您可以在将块加密后看到的长度为 0 字节,并且加密流有一些例外

Print screen from debugger

如何解决?请给我一些线索。

static class Aes
{
    public static string bytesToHexString(byte[] key)
    {
        return BitConverter.ToString(key).Replace("-", String.Empty);
    }

    public static byte[] stringToBytes(string key)
    {
        return Enumerable.Range(0, key.Length)
                 .Where(x => x % 2 == 0)
                 .Select(x => Convert.ToByte(key.Substring(x, 2), 16))
                 .ToArray();
    }

    public static void generateKeyAndIv(out byte[] key, out byte[] IV)
    {
        using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
        {
            aesAlg.BlockSize = 128;
            aesAlg.KeySize = 256;
            aesAlg.Padding = PaddingMode.None;
            //aesAlg.Mode = CipherMode.CBC;
            aesAlg.GenerateKey();
            aesAlg.GenerateIV();
            key = aesAlg.Key;
            IV = aesAlg.IV;
        }
    }
    public static string EncryptStringToString(string plainText, byte[] Key, byte[] IV)
    {
        byte[] bytes =EncryptStringToBytes_Aes(plainText, Key, IV);
        return Convert.ToBase64String(bytes);
        //return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
    }

    public static string DecryptStringToString(string cipherText, byte[] Key, byte[] IV)
    {
        //byte[] bytes = Encoding.UTF8.GetBytes(cipherText);
        byte[] bytes = Convert.FromBase64String(cipherText);
        return DecryptStringFromBytes_Aes(bytes, Key, IV);
    }

    public static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
    {
        // Check arguments. 
        if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("Key");
        /*byte[] encrypted;
        // Create an AesCryptoServiceProvider object 
        // with the specified key and IV. 
        using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
        {
            aesAlg.BlockSize = 128;
            aesAlg.KeySize = 256;
            aesAlg.Padding = PaddingMode.PKCS7;
            aesAlg.Mode = CipherMode.CBC;
            aesAlg.Key = Key;
            aesAlg.IV = IV;
            // Create a decrytor 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);
                    }
                }

                encrypted = msEncrypt.ToArray();
            }
        }*/
        byte[] encrypted;
        // Create an AesManaged object
        // with the specified key and IV.
        using (AesManaged aesAlg = new AesManaged())
        {

            // Create a decrytor to perform the stream transform.
            aesAlg.Padding = PaddingMode.None;
            aesAlg.BlockSize = 128;
            aesAlg.KeySize = 256;
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

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

        //return encrypted;


        // Return the encrypted bytes from the memory stream. 
        return encrypted;

    }

    public static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
    {
        // Check arguments. 
        if (cipherText == null || cipherText.Length <= 0)
            throw new ArgumentNullException("cipherText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV");

        // Declare the string used to hold 
        // the decrypted text. 
        string plaintext = null;

        // Create an AesCryptoServiceProvider object 
        // with the specified key and IV. 
        using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
        {
            aesAlg.BlockSize = 128;
            aesAlg.KeySize = 256;
            aesAlg.Padding = PaddingMode.PKCS7;
            aesAlg.Mode = CipherMode.CBC;
            aesAlg.Key = Key;
            aesAlg.IV = IV;
            // Create a decrytor to perform the stream transform.
            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

            // Create the streams used for decryption. 
            using (MemoryStream msDecrypt = new MemoryStream(cipherText))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {

                        // Read the decrypted bytes from the decrypting stream 
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }

        }

        return plaintext;

    }
}

好的,也许我会再解释一下整个情况。我写了自己的安全电子邮件。我有 2x 加密。服务器和客户端之间的通信使用 RSA 和 AES 加密。用户创建的消息也由 RSA + AES 加密。

发送消息如下:

  1. 客户端连接到服务器。
  2. 他们建立安全连接(服务器发送其公钥,客户端生成AES密钥通过服务器的公钥对其进行加密并将其发送到服务器。之后服务器和客户端使用AES密钥进行通信)。
  3. 客户端在 XML 中创建消息,消息可以包含读取到 base64 并使用 AES 加密的文件。
  4. 消息已写入 db。

收到消息的样子:

  1. 连接到服务器。
  2. 建立安全连接。
  3. 从服务器获取消息。
  4. 使用 RSA 私钥解密 AES 密钥。
  5. 使用解密的 AES 密钥解密消息。
  6. 如果有文件,则使用 AES 对其进行解密,然后将其 base64_decode 为字节并保存。

现在的问题在于大数据的加密。有时甚至 200-300 kB 也有问题。

我发现的另一件有趣的事情是,当我通过调试器运行代码时,它可以工作,但是当我在没有它的情况下运行代码时。它不起作用。


解决方案

我找到了问题的解决方案。因为我使用不同的密钥/ivs 垃圾收集器非常快地使用了两次 AES 加密/解密,所以垃圾收集器没有清理这些对象。解决方案是添加

GC.Collect();
GC.WaitForPendingFinalizers();

就在 DecryptStringFromBytes_Aes 和 EncryptStringToBytes_Aes 返回值之前

我希望它能帮助遇到和我一样问题的人。

【问题讨论】:

  • 如果你稍微整理一下你的实现,你会更容易找到问题。
  • 现在我尝试使用此处的msdn.microsoft.com/en-us/library/… 代码,这段代码给了我“填充无效”异常。因此,当我使用来自 MSDN 的示例代码时,要么我有一个空字符串,要么有时“填充无效异常”。
  • 我发现来自 msdn 的代码在需要解密/加密大量数据的情况下不起作用。
  • 你的代码也不能处理小数据,不知道为什么,我也看到了 Hans Passant 的评论,但它不起作用。从我看到的情况来看,您已经按照您的指示进行了接线。
  • 如果你有一个新问题,你需要作为一个不同的问题发布,即“加密大数据的问题”需要在一个新问题中发布,不要随着你的进步而改变这个问题这不是这个网站应该如何运作的。

标签: c# .net cryptography aes rijndaelmanaged


【解决方案1】:

您使用流的方式意味着没有数据写入底层流,我不知道为什么,但我确实找到了一种我已经重新设计的替代方法。

使用以下代码,您应该能够作为控制台应用程序运行,然后对其进行整形,使其适合您的目的。告诉我进展如何:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Encrypto
{
    public static class Program
    {
        public static void Main()
        {

            const string password = "test";
            const string text = "test";
            var cipherText = Aes.Encrypt(password, text);
            var decrypted = Aes.Decrypt(password, cipherText);

            Console.WriteLine(decrypted);
            Console.ReadKey();
        }
    }

    internal static class Aes
    {
        public static EncryptedData Encrypt(string password, string data)
        {
            return Transform(true, password, data, null) as EncryptedData;
        }

        public static string Decrypt(string password, EncryptedData data)
        {
            return Transform(false, password, data.DataString, data.SaltString) as string;
        }

        private static object Transform(bool encrypt, string password, string data, string saltString)
        {
            using (var aes = new AesManaged())
            {
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;
                var keyLen = aes.KeySize/8;
                var ivLen = aes.BlockSize/8;
                const int saltSize = 8;
                const int iterations = 8192;

                var salt = encrypt ? new byte[saltSize] : Convert.FromBase64String(saltString);
                if (encrypt)
                {
                    new RNGCryptoServiceProvider().GetBytes(salt);
                }

                var bcKey = new Rfc2898DeriveBytes("BLK" + password, salt, iterations).GetBytes(keyLen);
                var iv = new Rfc2898DeriveBytes("IV" + password, salt, iterations).GetBytes(ivLen);
                var macKey = new Rfc2898DeriveBytes("MAC" + password, salt, iterations).GetBytes(16);

                aes.Key = bcKey;
                aes.IV = iv;

                var rawData = encrypt ? Encoding.UTF8.GetBytes(data) : Convert.FromBase64String(data);

                using (var transform = encrypt ? aes.CreateEncryptor() : aes.CreateDecryptor())
                using (var memoryStream = encrypt ? new MemoryStream() : new MemoryStream(rawData))
                using (var cryptoStream = new CryptoStream(memoryStream, transform, encrypt ? CryptoStreamMode.Write : CryptoStreamMode.Read))
                {
                    if (encrypt)
                    {
                        cryptoStream.Write(rawData, 0, rawData.Length);
                        cryptoStream.FlushFinalBlock();
                        return new EncryptedData(salt, macKey, memoryStream.ToArray());
                    }
                    var originalData = new byte[rawData.Length];
                    var count = cryptoStream.Read(originalData, 0, originalData.Length);

                    return Encoding.UTF8.GetString(originalData, 0, count);
                }
            }
        }

        public class EncryptedData
        {
            public EncryptedData(byte[] salt, byte[] mac, byte[] data)
            {
                Salt = salt;
                MAC = mac;
                Data = data;
            }

            private byte[] Salt { get; set; }

            public string SaltString
            {
                get { return Convert.ToBase64String(Salt); }
            }

            private byte[] MAC { get; set; }

            private byte[] Data { get; set; }

            public string DataString
            {
                get { return Convert.ToBase64String(Data); }
            }
        }
    }
}

提供此答案的来源:Using AES encryption in .NET - CryptographicException saying the padding is invalid and cannot be removed

【讨论】:

  • 是一样的。我在第一篇文章中详细解释了我的整个问题。我不明白为什么我有问题,我使用了 MSDN 中的示例。
  • 好的,您从 MSDN 尝试的代码不适合您,它适合我,因此您需要仔细查看您拥有的内容,并可能尝试其他方法。您是在新的控制台项目中运行我链接的代码还是在您现有的应用程序中运行?
  • 您没有以与示例相同的方式使用代码,不同的位可能会导致您的问题,例如generateKeyAndIv()
  • 来自 MSDN 和您的代码正在运行,但是当我尝试加密大量数据时,它会出现“填充无效或已删除”错误。 generateKeyAndIV() 只分配我需要通过 RSA 加密并发送到服务器的密钥和 IV。
  • 我不能因为“声誉点数”哈哈 现在最好的事情是当我将解决方案移动到在 Windows 2008 RC 上运行的专用服务器时它不起作用。嗯
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多