我收到错误消息“填充无效且无法删除。”当我尝试在 AES 中解密时

问题描述 投票:0回答:1

尝试在 AES 中解密时出现错误“填充无效且无法删除。”

这是我用来加密和解密的代码:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;
using System.Xml;

namespace ClassLibrary
{
    public static class AesHelper
    {
        private const int KeySize = 256;
        private const int BlockSize = 128;
        private const int Iterations = 1000;

        /// <summary>
        /// AES encryption of a plain text string using the specified secret Key and IV Salt.  
        /// </summary>
        /// <param name="plainText">The plain text you wish to encrypt to cipher text.</param>
        /// <param name="key">The secret key.</param>
        /// <param name="iv">A secret salt for the initialization vector.</param>
        /// <returns>An encrypted cipher text</returns>
        public static string EncryptToAes(string plainText, string key, string iv)
        {
            byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
            byte[] keyBytes = Encoding.UTF8.GetBytes(key);
            byte[] ivBytes = Encoding.UTF8.GetBytes(iv);

            keyBytes = SHA256.Create().ComputeHash(keyBytes);

            byte[] bytesEncrypted = Encrypt(plainBytes, keyBytes, ivBytes);

            return Convert.ToBase64String(bytesEncrypted);
        }

        /// <summary>
        /// Decrypts the cipher text back to plain text using the same secret Key phrase and IV 
        /// Salt that was previously used to encrypt.
        /// </summary>
        /// <param name="cipherText">A string encrypted earlier using the same secret Key phrase and     IV Salt.</param>
        /// <param name="key">The secret Key.</param>
        /// <param name="iv">A secret salt for the initialization vector.</param>
        /// <returns>A plain text string.</returns>
        public static string DecryptToString(string cipherText, string key, string iv)
        {
            byte[] cipherBytes = Convert.FromBase64String(cipherText);
            byte[] keyBytes = Encoding.UTF8.GetBytes(key);
            byte[] ivBytes = Encoding.UTF8.GetBytes(iv);

            keyBytes = SHA256.Create().ComputeHash(keyBytes);

            byte[] bytesDecrypted = Decrypt(cipherBytes, keyBytes, ivBytes);

            return Encoding.UTF8.GetString(bytesDecrypted);
        }

        /// <summary>
        /// Creates a AES instance.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="iv"></param>
        /// <returns>A AES instance</returns>
        private static Aes CreateAesInstance(byte[] key, byte[] iv)
        {
            var aes = Aes.Create();

            aes.KeySize = KeySize;
            aes.BlockSize = BlockSize;
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.CBC;

            var derived = new Rfc2898DeriveBytes(key, iv, Iterations);
            aes.Key = derived.GetBytes(aes.KeySize / 8);
            aes.IV = derived.GetBytes(aes.BlockSize / 8);

            return aes;
        }

        /// <summary>
        /// Encrypts bytes with AES.
        /// </summary>
        /// <param name="bytesToBeEncrypted"></param>
        /// <param name="key"></param>
        /// <param name="iv"></param>
        /// <returns>The encrypted bytes</returns>
        private static byte[] Encrypt(byte[] bytesToBeEncrypted, byte[] key, byte[] iv)
        {
            byte[] encryptedBytes = null;

            using (var aes = CreateAesInstance(key, iv))
            {
                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
                        cs.FlushFinalBlock();
                    }

                    encryptedBytes = ms.ToArray();
                }
            }

            return encryptedBytes;
        }

        /// <summary>
        /// Decrypt encrypted bytes. 
        /// </summary>
        /// <param name="bytesToBeDecrypted"></param>
        /// <param name="key"></param>
        /// <param name="iv"></param>
        /// <returns>Decrypted bytes</returns>
        private static byte[] Decrypt(byte[] bytesToBeDecrypted, byte[] key, byte[] iv)
        {
            byte[] decryptedBytes = null;

            using (var aes = CreateAesInstance(key, iv))
            {
                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(ms, aes.CreateDecryptor(),  CryptoStreamMode.Write))
                    {
                        cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
                        cs.FlushFinalBlock();
                    }

                    decryptedBytes = TrimZeroPadding(ms.ToArray());
                }
            }

            return decryptedBytes;
        }

        /// <summary>
        /// Trims padding from byte array.
        /// </summary>
        /// <param name="array"></param>
        /// <returns>Byte array without padding.</returns>
        private static byte[] TrimZeroPadding(byte[] array)
        {
            if (array == null || array.Length == 0)
            {
                return null;
            }

            var lastZeroIndex = array.Length;
            for (int i = array.Length - 1; i >= 0; i--)
            {
                if (array[i] == char.MinValue)
                {
                    lastZeroIndex = i;
                }
                else
                {
                    break;
                }
            }

            return array.Where((item, index) => index < lastZeroIndex).ToArray();
        }

    }
}

密钥和 IV 都是硬编码的,所以它们不会出错,加密和解密方法使用:

CreateAesInstance

我将 paddign 设置为的位置:

PaddingMode.PKCS7;

所以我猜这也不是问题?

c# .net aes
1个回答
0
投票

错误“填充无效,无法删除。”当解密数据包含与预期填充不匹配的填充时发生。在您提供的代码中,使用的填充模式是 PKCS7,它向明文添加填充以确保它是块大小的倍数。解密时,填充会自动删除。但是,如果填充不正确,则无法删除,从而导致错误。

要修复这个错误,你应该确保用于加密和解密的密钥和IV是相同的。也有可能密文在传输过程中被修改或损坏,所以您应该验证密文没有损坏。此外,您可以修改 TrimZeroPadding 方法来检查填充字节并手动删除填充。

这是检查填充的 Decrypt 方法的更新版本:

private static byte[] Decrypt(byte[] bytesToBeDecrypted, byte[] key, byte[] iv)
{
    byte[] decryptedBytes = null;

    using (var aes = CreateAesInstance(key, iv))
    {
        using (var ms = new MemoryStream())
        {
            using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
            {
                cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
                cs.FlushFinalBlock();
            }

            var decrypted = ms.ToArray();

            var pad = decrypted[decrypted.Length - 1];
            var padCount = (int)pad;

            if (padCount > 0 && padCount < aes.BlockSize / 8)
            {
                var padded = true;

                for (var i = decrypted.Length - padCount; i < decrypted.Length - 1; i++)
                {
                    if (decrypted[i] != pad)
                    {
                        padded = false;
                        break;
                    }
                }

                if (padded)
                {
                    decryptedBytes = new byte[decrypted.Length - padCount];
                    Array.Copy(decrypted, 0, decryptedBytes, 0, decryptedBytes.Length);
                }
            }

            if (decryptedBytes == null)
            {
                decryptedBytes = decrypted;
            }
        }
    }

    return decryptedBytes;
}

您可以使用这个更新的 Decrypt 方法来解密没有填充错误的密文。

© www.soinside.com 2019 - 2024. All rights reserved.