【问题标题】:Rijndael Padding ErrorRijndael 填充错误
【发布时间】:2025-12-26 08:00:11
【问题描述】:

您好,我正在尝试通过 Rijaendal 加密/解密字符串。 我根本无法弄清楚为什么解密会爆炸。我总是以不正确的填充错误结束。让我失望的一件事是我作为 HEX 数组返回的加密结果。它的长度为 14 个字节。在我的解密函数中,从 HEX 转换后,相同的字节数组最终有 16 个字节。

任何帮助将不胜感激:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace rjandal
{
    class Program
    {
        static void Main(string[] args)
        {
            string DataForEncrypting = "this is a test";

            string key = string.Empty;
            string iv = string.Empty;

            using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
            {
                rmt.KeySize = 256;
                rmt.BlockSize = 128;
                rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
                rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
                rmt.GenerateKey();
                rmt.GenerateIV();
                key = Convert.ToBase64String(rmt.Key);
                iv = Convert.ToBase64String(rmt.IV);
            }

            string encryptedData = _encrypt(DataForEncrypting, key, iv);
            string unencryptedData = _decrypt(key, iv, HexString2Ascii(encryptedData));

            Console.WriteLine(unencryptedData);
            Console.WriteLine(encryptedData);
            Console.ReadKey();
        }

        private static string _encrypt(string value, string key, string initVector)
        {
            byte[] buffer = ASCIIEncoding.ASCII.GetBytes(value);
            byte[] encBuffer;
            using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
            {
                rmt.KeySize = 256;
                rmt.BlockSize = 128;
                rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
                rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
                encBuffer = rmt.CreateEncryptor(Convert.FromBase64String(key),
                    Convert.FromBase64String(initVector)).TransformFinalBlock(buffer, 0, buffer.Length);
            }
            string encryptValue = ConvertToHex(ASCIIEncoding.ASCII.GetString(encBuffer));
            return encryptValue;
        }

        private static string _decrypt(string key, string initVector, string value)
        {
            byte[] hexBuffer = ASCIIEncoding.ASCII.GetBytes(value);
            byte[] decBuffer;
            using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
            {
                rmt.KeySize = 256;
                rmt.BlockSize = 128;
                rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
                rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
                decBuffer = rmt.CreateDecryptor(Convert.FromBase64String(key),
                    Convert.FromBase64String(initVector)).TransformFinalBlock(hexBuffer, 0, hexBuffer.Length);
            }

            return System.Text.ASCIIEncoding.ASCII.GetString(decBuffer);
        } 

        private static string ConvertToHex(string asciiString)
        {
            string hex = "";
            foreach (char c in asciiString)
            {
                int tmp = c;
                hex += String.Format("{0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString()));
            }
            return hex;
        }

        private static string HexString2Ascii(string hexString)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i <= hexString.Length - 2; i += 2)
            {
                sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hexString.Substring(i, 2), System.Globalization.NumberStyles.HexNumber))));
            }
            return sb.ToString();
        }

    }
}

【问题讨论】:

    标签: c# encryption aes rijndael


    【解决方案1】:

    基本上,您在文本和数据之间进行了太多转换。看看这个,例如:

    string encryptValue = ConvertToHex(ASCIIEncoding.ASCII.GetString(encBuffer));
    

    一旦你有了一个 ASCII 字符串,你为什么需要把 that 转换成十六进制?已经是文字了!但是到那时你已经丢失了数据。除非你真的需要它是十六进制的(在这种情况下遵循 Adam 的建议并改变你的 HexToAscii 方法来获取一个字节 [] 而不是一个字符串)你应该只使用 Convert.ToBase64String:

    string encryptValue = Convert.ToBase64String(encBuffer);
    

    解密时在另一端使用Convert.FromBase64String。然后你就可以完全摆脱你的十六进制方法了。

    哦,一般来说我不会使用Encoding.ASCII 开头...我几乎总是使用Encoding.UTF8 来代替。目前,您将无法(正确)加密任何包含非 ASCII 字符(如重音符号)的字符串。

    这是您的测试程序的重新调整版本,其中做了一些更改。请注意,“密文”和“纯文本”的名称是在加密方面......它们仍然是二进制数据而不是文本!

    using System;
    using System.Security.Cryptography;
    using System.Text;
    
    class Program
    {
        static void Main(string[] args)
        {
            string DataForEncrypting = "this is a test";
    
            string key = string.Empty;
            string iv = string.Empty;
    
            using (RijndaelManaged rmt = new RijndaelManaged())
            {
                rmt.KeySize = 256;
                rmt.BlockSize = 128;
                rmt.Mode = CipherMode.CBC;
                rmt.Padding = PaddingMode.ISO10126;
                rmt.GenerateKey();
                rmt.GenerateIV();
                key = Convert.ToBase64String(rmt.Key);
                iv = Convert.ToBase64String(rmt.IV);
            }
    
            string encryptedData = _encrypt(DataForEncrypting, key, iv);
            string unencryptedData = _decrypt(key, iv, encryptedData);
    
            Console.WriteLine(unencryptedData);
            Console.WriteLine(encryptedData);
            Console.ReadKey();
        }
    
        private static string _encrypt(string value, string key, string initVector)
        {
            using (RijndaelManaged rmt = new RijndaelManaged())
            {
                rmt.KeySize = 256;
                rmt.BlockSize = 128;
                rmt.Mode = CipherMode.CBC;
                rmt.Padding = PaddingMode.ISO10126;
                byte[] plainText = Encoding.UTF8.GetBytes(value);
                byte[] cipherText = rmt.CreateEncryptor(Convert.FromBase64String(key),
                                                        Convert.FromBase64String(initVector))
                                       .TransformFinalBlock(plainText, 0, plainText.Length);
                return Convert.ToBase64String(cipherText);
            }
        }
    
        private static string _decrypt(string key, string initVector, string value)
        {
            using (RijndaelManaged rmt = new RijndaelManaged())
            {
                rmt.KeySize = 256;
                rmt.BlockSize = 128;
                rmt.Mode = CipherMode.CBC;
                rmt.Padding = PaddingMode.ISO10126;
                byte[] cipherText = Convert.FromBase64String(value);
                byte[] plainText = rmt.CreateDecryptor(Convert.FromBase64String(key),
                                                       Convert.FromBase64String(initVector))
                                      .TransformFinalBlock(cipherText, 0, cipherText.Length);
                return Encoding.UTF8.GetString(plainText);
            }
        }
    }
    

    【讨论】:

    • 虽然这是我在不需要十六进制值时使用的解决方案,但 OP 确实声明他需要十六进制值,而这会以 base64 形式返回它。
    • @Adam:直到现在我才看到他对你的回答的评论......(不在问题中)
    【解决方案2】:

    您不应该使用 ASCII 字符编码作为中间步骤;您应该将您的函数从十六进制更改为 ASCII(然后再返回),以从 byte[] 更改为十六进制(然后再返回)。

        private static string ConvertToHex(byte[] data)
        {
            string hex = "";
            foreach (byte b in data)
            {
                hex += b.ToString("X2");
            }
            return hex;
        }
    
        private static byte[] HexString2ByteArray(string hexString)
        {
            byte[] output = new byte[hexString.Length / 2];
    
            for (int i = 0; i <= hexString.Length - 2; i += 2)
            {
                 output[i/2] = Convert.ToByte(hexString.Substring(i, 2), 16);
            }
            return output;
        }
    

    作为旁注,您是否有理由寻找数组的十六进制表示而不是像 Base64 这样更紧凑的东西?您在示例中使用 Base64 来传输密钥和 IV,所以我只是想知道是什么让您想要在此处以十六进制返回加密数据。

    无论如何,这里有一些适合你的东西:

        private static string _encrypt(string value, string key, string initVector)
        {
            byte[] buffer = Encoding.Unicode.GetBytes(value);
            byte[] encBuffer;
            using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
            {
                rmt.KeySize = 256;
                rmt.BlockSize = 128;
                rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
                rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
                encBuffer = rmt.CreateEncryptor(Convert.FromBase64String(key),
                    Convert.FromBase64String(initVector)).TransformFinalBlock(buffer, 0, buffer.Length);
            }
            string encryptValue = ConvertToHex(encBuffer);
            return encryptValue;
        }
    
        private static string _decrypt(string key, string initVector, string value)
        {
            byte[] hexBuffer = HexString2ByteArray(value);
            byte[] decBuffer;
            using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
            {
                rmt.KeySize = 256;
                rmt.BlockSize = 128;
                rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
                rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
                decBuffer = rmt.CreateDecryptor(Convert.FromBase64String(key),
                    Convert.FromBase64String(initVector)).TransformFinalBlock(hexBuffer, 0, hexBuffer.Length);
            }
    
            return Encoding.Unicode.GetString(decBuffer);
        } 
    

    【讨论】:

    • 原因是我被要求提供base64的IV和Key,以及hex的加密结果。
    • @jcs:够公平的;这应该为您解决问题。请注意,Jon 的答案虽然有效,但会将加密值返回为 base64 字符串,而不是十六进制。
    • 谢谢大家的回答,这很有帮助。谢谢
    【解决方案3】:

    您可以避免解密/加密和使用 System.Text.Encoding 的问题,并避免使用 Base64 编码解决方法,方法是添加一些完全绕过 Microsoft 在 System.Text.Encoding 中的不匹配转换的方法,允许您加密内存中的真实字节,无需任何翻译。

    由于使用了这些,我避免了由 System.Text.Encoding 方法引起的填充错误,也没有使用 Base64 转换。

        private static Byte[] GetBytes(String SomeString)
        {
            Char[] SomeChars = SomeString.ToCharArray();
            Int32 Size = SomeChars.Length * 2;
            List<Byte> TempList = new List<Byte>(Size);
            foreach (Char Character in SomeChars)
            {
                TempList.AddRange(BitConverter.GetBytes(Character));
            }
            return TempList.ToArray();
        }
        private static String GetString(Byte[] ByteArray)
        {
            Int32 Size = ByteArray.Length / 2;
            List<Char> TempList = new List<Char>(Size);
            for (Int32 i = 0; i < ByteArray.Length; i += 2)
            {
                TempList.Add(BitConverter.ToChar(ByteArray, i));
            }
            return new String(TempList.ToArray());
        }
    

    以及它们如何与加密一起使用

        private static String Encrypt(String Test1, Byte[] Key, Byte[] IV)
        {
            Byte[] Encrypted;
            using (AesCryptoServiceProvider AesMan = new AesCryptoServiceProvider())
            {
                AesMan.Mode              = CipherMode.CBC;
                AesMan.Padding           = PaddingMode.ISO10126;
                ICryptoTransform EncThis = AesMan.CreateEncryptor(Key, IV);
    
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, EncThis, CryptoStreamMode.Write))
                    {
    
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            //Write all data to the stream.
                            swEncrypt.Write(Test1);
                        }
                        Encrypted = msEncrypt.ToArray();
                    }
                }
            };
            return GetString(Encrypted);
        }
    
        private static String Decrypt(String Data, Byte[] Key, Byte[] IV)
        {
            String Decrypted;
            using (AesCryptoServiceProvider AesMan = new AesCryptoServiceProvider())
            {
                AesMan.Mode              = CipherMode.CBC;
                AesMan.Padding           = PaddingMode.ISO10126;
                ICryptoTransform EncThis = AesMan.CreateDecryptor(Key, IV);
    
                using (MemoryStream msDecrypt = new MemoryStream(GetBytes(Data)))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, EncThis, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {
    
                            // Read the decrypted bytes from the decrypting stream 
                            // and place them in a string.
                            Decrypted = srDecrypt.ReadToEnd();
                        }
                    }
                }
            }
            return Decrypted;
        }
    

    【讨论】: