在.NET中使用AES加密 - CryptographicException称填充是无效的不能删除 [英] Using AES encryption in .NET - CryptographicException saying the padding is invalid and cannot be removed

查看:1335
本文介绍了在.NET中使用AES加密 - CryptographicException称填充是无效的不能删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一些AES加密code在C#中,我无法得到它来加密和解密正常。如果我输入test作为密码和这个数据必须保密,从每个人!我收到以下异常:

  System.Security.Cryptography.CryptographicException:填充是无效的不能删除。
   在System.Security.Cryptography.RijndaelManagedTransform.DecryptData(字节[] INPUTBUFFER,的Int32 inputOffset,的Int32 inputCount,字节[]放大器; OUTPUTBUFFER,的Int32 outputOffset,PaddingMode paddingMode,布尔fLast)
   在System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(字节[] INPUTBUFFER,的Int32 inputOffset,的Int32 inputCount)
   在System.Security.Cryptography.CryptoStream.FlushFinalBlock()
   在System.Security.Cryptography.CryptoStream.Dispose(布尔处置)
   在System.IO.Stream.Close()
   在System.IO.Stream.Dispose()
   ...
 

如果我输入的东西少于16个字符,我没有得到任何输出。

我相信我需要一些特殊的处理,在加密,因为AES是一种分组密码,但我不知道那是什么,我是不是能在网上找到展示了如何在任何例子。这是我的code:

 使用系统;
使用System.IO;
使用System.Security.Cryptography;
使用System.Text;

公共静态类DatabaseCrypto
{
    公共静态的EncryptedData加密(字符串密码,字符串数据)
    {
        返回DatabaseCrypto.Transform(真,密码,数据,NULL,NULL)作为的EncryptedData;
    }

    公共静态字符串解密(串密码的EncryptedData数据)
    {
        返回DatabaseCrypto.Transform(假,密码,data.DataString,data.SaltString,data.MACString)的字符串;
    }

    私有静态对象的变换(布尔加密,密码字符串,字符串数据,字符串saltString,串macString)
    {
        使用(AesManaged AES =新AesManaged())
        {
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;
            INT key_len = aes.KeySize / 8;
            INT iv_len = aes.BlockSize / 8;
            const int的salt_size = 8;
            const int的迭代= 8192;

            byte []的盐=加密?新的字节[salt_size]:Convert.FromBase64String(saltString);
            如果(加密)
            {
                新RNGCryptoServiceProvider()的GetBytes(盐);
            }

            byte []的bc_key =新Rfc2898DeriveBytes(BLK+口令,盐,迭代).GetBytes(key_len);
            字节[] IV =新Rfc2898DeriveBytes(IV+口令,盐,迭代).GetBytes(iv_len);
            字节[]的mac_key =新Rfc2898DeriveBytes(陆委会+口令,盐,迭代).GetBytes(16);

            aes.Key = bc_key;
            aes.IV = IV;

            byte []的RAWDATA =加密? Encoding.UTF8.GetBytes(数据):Convert.FromBase64String(数据);

            使用(ICryptoTransform的变换=加密aes.CreateEncryptor():aes.CreateDecryptor())
            使用(MemoryStream的MemoryStream的=加密新的MemoryStream():新的MemoryStream(RAWDATA))
            使用(CryptoStream的CryptoStream的=新的CryptoStream(MemoryStream的,变换,加密CryptoStreamMode.Write:CryptoStreamMode.Read))
            {
                如果(加密)
                {
                    cryptoStream.Write(RAWDATA,0,rawData.Length);

                    返回新的EncryptedData(盐的mac_key,memoryStream.ToArray());
                }
                其他
                {
                    byte []的originalData =新的字节[rawData.Length]
                    诠释计数= cryptoStream.Read(originalData,0,originalData.Length);

                    返回Encoding.UTF8.GetString(originalData,0,计数);
                }
            }
        }
    }
}

公共类的EncryptedData
{
    公众的EncryptedData()
    {
    }

    公众的EncryptedData(byte []的盐,byte []的MAC,byte []的数据)
    {
        this.Salt =盐;
        this.MAC = MAC;
        this.Data =数据;
    }

    公众的EncryptedData(字符串盐,串MAC,字符串数据)
    {
        this.SaltString =盐;
        this.MACString = MAC;
        this.DataString =数据;
    }

    公共byte []的盐
    {
        得到;
        组;
    }

    公共字符串SaltString
    {
        {返回Convert.ToBase64String(this.Salt); }
        集合{this.Salt = Convert.FromBase64String(值); }
    }

    公共byte []的MAC
    {
        得到;
        组;
    }

    公共字符串MACString
    {
        {返回Convert.ToBase64String(this.MAC); }
        集合{this.MAC = Convert.FromBase64String(值); }
    }

    公共byte []的数据
    {
        得到;
        组;
    }

    公共字符串DataString
    {
        {返回Convert.ToBase64String(this.Data); }
        集合{this.Data = Convert.FromBase64String(值); }
    }
}

    静态无效ReadTest()
    {
        Console.WriteLine(请输入密码:);
        串密码=到Console.ReadLine();

        使用(StreamReader的读者=新的StreamReader(aes.cs.txt))
        {
            的EncryptedData ENC =新的EncryptedData();
            enc.SaltString = reader.ReadLine();
            enc.MACString = reader.ReadLine();
            enc.DataString = reader.ReadLine();

            Console.WriteLine(解密的数据为:+ DatabaseCrypto.Decrypt(密码,ENC));
        }
    }

    静态无效WriteTest()
    {
        Console.WriteLine(请输入数据:);
        字符串数据=到Console.ReadLine();
        Console.WriteLine(请输入密码:);
        串密码=到Console.ReadLine();

        的EncryptedData ENC = DatabaseCrypto.Encrypt(口令,数据);

        使用(StreamWriter的流=新的StreamWriter(aes.cs.txt))
        {
            stream.WriteLine(enc.SaltString);
            stream.WriteLine(enc.MACString);
            stream.WriteLine(enc.DataString);

            Console.WriteLine(加密的数据为:+ enc.DataString);
        }
    }
 

解决方案

在使用如AES的模式的块密码,需要填充,如CBC,你必须知道的输出永远是块大小的倍数。要做到这一点,填充模式,如PKCS7将一些字节添加到加密的在加密过程的的末端。但是,你必须让当最终发生加密知道。要做到这一点,你所要做的就是插入语句

  cryptoStream.FlushFinalBlock();
 

  cryptoStream.Write(RAWDATA,0,rawData.Length);
 

PS:

也许这只是用于调试,但你的盐生成方法每次产生完全相同的盐。

I wrote some AES encryption code in C# and I am having trouble getting it to encrypt and decrypt properly. If I enter "test" as the passphrase and "This data must be kept secret from everyone!" I receive the following exception:

System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed.
   at System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast)
   at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
   at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
   at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing)
   at System.IO.Stream.Close()
   at System.IO.Stream.Dispose()
   ...

And if I enter something less than 16 characters I get no output.

I believe I need some special handling in the encryption since AES is a block cipher, but I'm not sure exactly what that is, and I wasn't able to find any examples on the web showing how. Here is my code:

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

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

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

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

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

            byte[] bc_key = new Rfc2898DeriveBytes("BLK" + password, salt, iterations).GetBytes(key_len);
            byte[] iv = new Rfc2898DeriveBytes("IV" + password, salt, iterations).GetBytes(iv_len);
            byte[] mac_key = new Rfc2898DeriveBytes("MAC" + password, salt, iterations).GetBytes(16);

            aes.Key = bc_key;
            aes.IV = iv;

            byte[] rawData = encrypt ? Encoding.UTF8.GetBytes(data) : Convert.FromBase64String(data);

            using (ICryptoTransform transform = encrypt ? aes.CreateEncryptor() : aes.CreateDecryptor())
            using (MemoryStream memoryStream = encrypt ? new MemoryStream() : new MemoryStream(rawData))
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, encrypt ? CryptoStreamMode.Write : CryptoStreamMode.Read))
            {
                if (encrypt)
                {
                    cryptoStream.Write(rawData, 0, rawData.Length);

                    return new EncryptedData(salt, mac_key, memoryStream.ToArray());
                }
                else
                {
                    byte[] originalData = new byte[rawData.Length];
                    int count = cryptoStream.Read(originalData, 0, originalData.Length);

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

public class EncryptedData
{
    public EncryptedData()
    {
    }

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

    public EncryptedData(string salt, string mac, string data)
    {
        this.SaltString = salt;
        this.MACString = mac;
        this.DataString = data;
    }

    public byte[] Salt
    {
        get;
        set;
    }

    public string SaltString
    {
        get { return Convert.ToBase64String(this.Salt); }
        set { this.Salt = Convert.FromBase64String(value); }
    }

    public byte[] MAC
    {
        get;
        set;
    }

    public string MACString
    {
        get { return Convert.ToBase64String(this.MAC); }
        set { this.MAC = Convert.FromBase64String(value); }
    }

    public byte[] Data
    {
        get;
        set;
    }

    public string DataString
    {
        get { return Convert.ToBase64String(this.Data); }
        set { this.Data = Convert.FromBase64String(value); }
    }
}

    static void ReadTest()
    {
        Console.WriteLine("Enter password: ");
        string password = Console.ReadLine();

        using (StreamReader reader = new StreamReader("aes.cs.txt"))
        {
            EncryptedData enc = new EncryptedData();
            enc.SaltString = reader.ReadLine();
            enc.MACString = reader.ReadLine();
            enc.DataString = reader.ReadLine();

            Console.WriteLine("The decrypted data was: " + DatabaseCrypto.Decrypt(password, enc));
        }
    }

    static void WriteTest()
    {
        Console.WriteLine("Enter data: ");
        string data = Console.ReadLine();
        Console.WriteLine("Enter password: ");
        string password = Console.ReadLine();

        EncryptedData enc = DatabaseCrypto.Encrypt(password, data);

        using (StreamWriter stream = new StreamWriter("aes.cs.txt"))
        {
            stream.WriteLine(enc.SaltString);
            stream.WriteLine(enc.MACString);
            stream.WriteLine(enc.DataString);

            Console.WriteLine("The encrypted data was: " + enc.DataString);
        }
    }

解决方案

When using a block cipher like AES in a mode that requires padding, like CBC, you must be aware that the output will always be a multiple of the block size. To accomplish this, padding modes like PKCS7 will add some bytes to the cipher at the end of the encryption process. But you have to let the encryptor know when the end occurs. To do so, all you have to do is insert the statement

cryptoStream.FlushFinalBlock();  

after

cryptoStream.Write(rawData, 0, rawData.Length);

PS:

Perhaps it is just for debugging, but your salt generation method generates the exact same salt every time.

这篇关于在.NET中使用AES加密 - CryptographicException称填充是无效的不能删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆