"填充无效,无法删除"例外

时间:2017-03-06 17:39:10

标签: c# exception encryption padding

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

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.
        }
    }

当我运行此程序时,控制台上显示“填充无效且无法删除”,我知道加密成功(我看到它在通过服务器时加密)但是当我尝试解密时写下例外。

1 个答案:

答案 0 :(得分:1)

执行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位字符。使用{{ 1}}相反,它是一个更常见的编码,如果你不使用任何特殊字符仍然会占用相同数量的空间,但如果你最终在你的字符串中使用它们就不会丢失字符你将使用ASCII编码)

更新:您的代码存在第二个问题。您从未在发送方或接收方设置Enocding.UTF8。如果您没有明确指定IV,它将使用随机生成的IV。修复MemoryStream错误和IV错误将使代码工作。

以下是一个完整的示例,您甚至可以see run online

desObj.IV