"Padding is invalid and cannot be removed" 异常

"Padding is invalid and cannot be removed" exception

我在这里看到很多人遇到这个异常的问题,但我根本不明白解决方案。 简短回顾: 我的程序是 C# 中的 GUI 程序,它假设在本地网络中的两台计算机上 运行 并且始终同步,因此,您会收到某种共享 space 以与另一台计算机上的朋友一起工作。 我的密码学在一个单例实例中,它负责我的程序的通信。以下是加密和解密数据的函数,它们接收数据作为文本或字节数组,return 将其作为字节数组或文本(取决于哪个,decrypt/encrypt):

public static byte[] Encrypt(string text)
    {
        desObj = Rijndael.Create();
        byte[] cipherBytes;
        byte[] plainBytes;
        byte[] plainKey;
        plainBytes = Encoding.ASCII.GetBytes(text);
        plainKey= Encoding.ASCII.GetBytes("0123456789abcdef");//Change the key.
        desObj.Key = plainKey;
        desObj.Mode = CipherMode.CFB;
        desObj.Padding = PaddingMode.PKCS7;
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, desObj.CreateEncryptor(), CryptoStreamMode.Write);
        cs.Write(plainBytes, 0, plainBytes.Length);
        cs.Close();
        cipherBytes = ms.ToArray();
        ms.Close();
        return cipherBytes;
    }
    public static string Decrypt(byte[] x)
    {
        byte[] plainBytes;
        byte[] plainKey;
        desObj = Rijndael.Create();
        plainKey = Encoding.ASCII.GetBytes("0123456789abcdef");//Change the key.
        desObj.Key = plainKey;
        desObj.Mode = CipherMode.CFB;
        desObj.Padding = PaddingMode.PKCS7;
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, desObj.CreateDecryptor(), CryptoStreamMode.Read);
        cs.Read(x, 0, x.Length);
        plainBytes = ms.ToArray();
        cs.Close();
        ms.Close();
        return Encoding.ASCII.GetString(plainBytes);
    }

public static void RecievingMessage()
    {
        try
        {
            if (srvr != null && openForms != null)//openForms is a list of all the current open forms.
            {
                byte[] data = new byte[1024];
                int i = 0;
                string[] message;
                if (srvr != null)
                    while (true)
                    {
                        Thread.Sleep(20);
                        int bytesRec = srvr.Receive(data);
                        message = Decrypt(data).Split(' ');
                        for (i = 0; i < openForms.Count; i++)
                        {
                            if (message[0].Equals(openForms[i].Name))
                            {
                                openForms[i].Recieve(message);
                            }
                        }
                    }
            }
        }
        ....//Catch clauses.
    }
public static void SendMessage(string sender, string message)
    {
        if (srvr != null)
        {
            try
            {
                srvr.Send(Encrypt(sender + " " + message + " "));
            }
            ...//Catch clauses.
        }
    }

当我运行这个程序时,控制台显示“Padding is invalid and cannot be removed”,我知道加密成功(我看到它在通过服务器时加密)但是当我尝试解密时,它会写入异常。

cs.Read(x, 0, x.Length) 并不是正确的做法。您需要将字节数组放入内存流的构造函数中,然后在循环中使用 cs.Read( 将数据读出到字符串中,直到读取所有字节。

public static string Decrypt(byte[] x)
{
    StringBuilder plainString = new StringBuilder();
    byte[] plainBytes = new byte[2048];
    byte[] plainKey;
    using(var desObj = Rijndael.Create()) //this should be a local variable and be disposed of.
    {
        plainKey = Encoding.ASCII.GetBytes("0123456789abcdef");//Change the key.
        desObj.Key = plainKey;
        desObj.Mode = CipherMode.CFB;
        desObj.Padding = PaddingMode.PKCS7;
        using(MemoryStream ms = new MemoryStream(x)) //pass the byte[] in to the memory stream.
        using(CryptoStream cs = new CryptoStream(ms, desObj.CreateDecryptor(), CryptoStreamMode.Read)) //this should be disposed of instead of calling .Close manually.
        {
             int bytesRead;
             while((bytesRead = cs.Read(plainBytes, 0, plainBytes.Lenght)) > 0)
             {
                 var str = Encoding.ASCII.GetString(plainBytes, 0, bytesRead);
                 plainString.Append(str);
             }
        }
    }
    return str.ToString();
}

或将其包装在 StreamReader 中以使代码更简单。

public static string Decrypt(byte[] x)
{
    byte[] plainKey;
    using(var desObj = Rijndael.Create()) //this should be a local variable and be disposed of.
    {
        plainKey = Encoding.ASCII.GetBytes("0123456789abcdef");//Change the key.
        desObj.Key = plainKey;
        desObj.Mode = CipherMode.CFB;
        desObj.Padding = PaddingMode.PKCS7;
        using(MemoryStream ms = new MemoryStream(x)) //pass the byte[] in to the memory stream.
        using(CryptoStream cs = new CryptoStream(ms, desObj.CreateDecryptor(), CryptoStreamMode.Read)) //this should be disposed of instead of calling .Close manually.
        using(StreamReader sr = new StreamReader(cs, Encoding.ASCII))
        {
            return sr.ReadToEnd();
        }
    }
}

(注意:出于类似的原因,我建议将您的加密代码更改为 StreamWriter。此外,ASCII 是一个糟糕的编码选择,它仅支持 7 位字符。改用 Enocding.UTF8,它是一种更常见的编码,如果您不使用任何特殊字符,它仍将占用相同数量的 space,但如果您不丢失字符,像使用 ASCII 编码一样最终将它们包含在您的字符串中)


更新:您的代码存在第二个问题。您永远不会在发送方或接收方设置 desObj.IV 。如果您没有明确分配一个,它将使用随机生成的 IV。修复 MemoryStream 错误和 IV 错误将使代码正常工作。

这是一个完整的例子,你甚至可以 see run online

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

public class Program
{
    public static void Main()
    {

        var key = Encoding.ASCII.GetBytes("0123456789abcdef");//Change the key.

        var data = Encrypt("Hello World",key);
        var str = Decrypt(data, key);
        Console.WriteLine(str);
    }

    public static byte[] Encrypt(string plaintext, byte[] key)
    {

        using(var desObj = Rijndael.Create())
        {
            desObj.Key = key;
            desObj.Mode = CipherMode.CFB;
            desObj.Padding = PaddingMode.PKCS7;
            using(var ms = new MemoryStream())
            {
                //Append the random IV that was generated to the front of the stream.
                ms.Write(desObj.IV, 0, desObj.IV.Length);

                //Write the bytes to be encrypted.
                using(CryptoStream cs = new CryptoStream(ms, desObj.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    var plainTextBytes = Encoding.UTF8.GetBytes(plaintext);
                    cs.Write(plainTextBytes, 0, plainTextBytes.Length);
                }
                return ms.ToArray();
            }
        }
    }

    public static string Decrypt(byte[] cyphertext, byte[] key)
    {

        using(MemoryStream ms = new MemoryStream(cyphertext))
        using(var desObj = Rijndael.Create())
        {
            desObj.Key = key;
            desObj.Mode = CipherMode.CFB;       
            desObj.Padding = PaddingMode.PKCS7;

            //Read the IV from the front of the stream and assign it to our object.
            var iv = new byte[16];
            var offset = 0;
            while(offset < iv.Length)
            {
                offset += ms.Read(iv, offset, iv.Length - offset);
            }
            desObj.IV = iv;

            //Read the bytes to be decrypted
            using(var cs = new CryptoStream(ms, desObj.CreateDecryptor(), CryptoStreamMode.Read))
            using(var sr = new StreamReader(cs, Encoding.UTF8))
            {
                return sr.ReadToEnd();
            }
        }
    }
}