【问题标题】:Keep getting "CryptographicException : Padding is invalid and cannot be removed." errors with AES CBC encryption even with padding不断收到“CryptographicException:填充无效且无法删除”。即使有填充,AES CBC 加密也会出错
【发布时间】:2019-11-10 23:24:00
【问题描述】:

我已经查看了有关此主题的其他帖子,但似乎无法找出我的错误。我正在尝试使用 AES CBC 流式传输加密文件(稍后我将执行其他操作)。我知道我需要使用 CBC 进行填充并进行配置。但是我不断收到同样的错误。

 Message: 
 System.Security.Cryptography.CryptographicException : Padding is invalid and cannot be removed.
     Stack Trace: 
  UniversalCryptoDecryptor.DepadBlock(Byte[] block, Int32 offset, Int32 count)
  UniversalCryptoDecryptor.UncheckedTransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
  UniversalCryptoTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
  CryptoStream.ReadAsyncCore(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken, Boolean useAsync)
  CryptoStream.Read(Byte[] buffer, Int32 offset, Int32 count)
  EncryptionFunctions.AESDecryptCBC(String encryptedFile, String plainTextFile, Byte[] key, Byte[] iv, Int32 blockSize, Int32 bufferSize) line 82
  EncryptionFunctionsUnitTests.TestEncryptAndDecryptFiles() line 39

我尝试了各种更改,例如不同类型的填充,但我得到的最好的结果是某种类型的垃圾输出。加密似乎工作正常,虽然我不能真正告诉它输出车库数据,因为它看起来像一堆汉字。输入文件只是我用于测试的 3 KB 维基百科文本。

这是我用于加密和解密的两个函数。

 public static void AESEncryptCBC(string plainTextFile, string encryptedFile, byte[] key, byte[] iv, int bufferSize = 65536)
    {
        using (FileStream fileStreamOutput = new FileStream(encryptedFile, FileMode.Create)) {
            using (FileStream fileStreamInput = new FileStream(plainTextFile, FileMode.Open))
            {
                using (Aes aes = Aes.Create())
                {
                    aes.Key = key;
                    aes.KeySize = key.Length*8; // Keysize is in bits, bytes to bits conversion
                    aes.BlockSize = 128; // bits
                    aes.Mode = CipherMode.CBC;
                    aes.IV = iv;
                    aes.Padding = PaddingMode.PKCS7;

                    using (CryptoStream cryptoStream = new CryptoStream(fileStreamOutput, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        int read;
                        byte[] readBuffer = new byte[bufferSize];

                        while ((read = fileStreamInput.Read(readBuffer, 0, readBuffer.Length)) > 0)
                        {
                            cryptoStream.Write(readBuffer, 0, read);
                        }
                    }
                }
            }
        }

    }

    public static void AESDecryptCBC(string encryptedFile, string plainTextFile, byte[] key, byte[] iv, int bufferSize = 65536)
    {
        using (FileStream fileStreamOutput = new FileStream(plainTextFile, FileMode.Create))
        {
            using (FileStream fileStreamInput = new FileStream(encryptedFile, FileMode.Open))
            {
                using (Aes aes = Aes.Create())
                {
                    aes.Key = key;
                    aes.KeySize = key.Length * 8; // Keysize is in bits, bytes to bits conversion
                    aes.BlockSize = 128; // bits
                    aes.Mode = CipherMode.CBC;
                    aes.IV = iv;
                    aes.Padding = PaddingMode.PKCS7;

                    using (CryptoStream cryptoStream = new CryptoStream(fileStreamInput, aes.CreateDecryptor(), CryptoStreamMode.Read))
                    {
                        int read;
                        byte[] readBuffer = new byte[bufferSize];

                        while ((read = cryptoStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
                        {
                            fileStreamOutput.Write(readBuffer, 0, read);
                        }
                    }
                }
            }
        }
    }

我调用它们的单元测试方法。不要介意随机数生成器,我稍后会使用哈希函数作为密码,这是为了确保我拥有正确大小的密钥。

 public void TestEncryptAndDecryptFiles()
    {
        string outFile = "out.txt";
        string outFile2 = "out2.txt";

        byte[] salt = new byte[128];
        RandomNumberGenerator.Fill(salt);

        int numberOfBits = 256;
        int blockSize = 128;
        byte[] key = new byte[numberOfBits / 8];
        byte[] iv = new byte[blockSize /8];
        RandomNumberGenerator.Fill(key);
        RandomNumberGenerator.Fill(iv);
        EncryptionFunctions.AESEncryptCBC(SampleText, outFile, key, iv);
        Assert.IsTrue(File.Exists(outFile));

        EncryptionFunctions.AESDecryptCBC(outFile, outFile2, key, iv);
        Assert.IsTrue(File.Exists(outFile2));
        Assert.AreEqual(HashFunctions.Md5(SampleText), HashFunctions.Md5(outFile2));

    }
}

【问题讨论】:

    标签: c# encryption .net-core aes encryption-symmetric


    【解决方案1】:

    当您设置 KeySize 属性时,它会生成一个新键。当您设置 Key 属性时,它会更新 KeySize 以匹配设置的内容。

    您不需要同时设置它们,并且由于您在加密和解密中都将 KeySize 设置为第二,因此您使用随机密钥进行加密并使用不同的随机密钥进行解密。

    调用CreateEncryptor(key, iv) 可以通过不重新读取不再重要的属性来解决这个问题。

    【讨论】:

    • 那真的很烦人。修改密钥大小以生成新密钥不在 AES 类的 Microsoft 网站上的任何文档中。由于它是一个属性,我不希望修改一个属性来重新初始化另一个。
    • @dxk3355 AesCryptoServiceProvider.KeySize - Remarks 确实提到了它,但它并不特定于该类。该行为来自SymmetricAlgorithm.KeySize,它会擦除​​键值(使下一个属性得到调用GenerateKey 隐式)。
    • @dxk3355 现在 SymmetricAlgorithm.KeySize 也这么说了:docs.microsoft.com/en-us/dotnet/api/…
    • 抱歉耽搁了,但感谢您更新文档。
    【解决方案2】:

    好的,我需要更改我的代码以使用aes.CreateEncryptor(key,iv)。我终于发现调用CreateEncryptor()CreateEncryptor(null, null) 会自动生成一个初始化向量和一个覆盖我用aes.keyaes.iv 设置的键的键。有点愚蠢,我可以设置它们,然后用一个显然不会这样做的函数覆盖它。也许这是一个错误,因为一页确实说“使用当前的密钥属性和初始化向量 (IV) 创建一个对称加密器对象”。它不是根据我之前的代码这样做的。

    反正我是在 https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.symmetricalgorithm.generateiv?view=netframework-4.8#System_Security_Cryptography_SymmetricAlgorithm_GenerateIV

    【讨论】:

      猜你喜欢
      • 2011-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-18
      • 1970-01-01
      • 2018-07-17
      • 1970-01-01
      相关资源
      最近更新 更多