Reputation: 23
I have seen many people's questions who met this exception here but I simply did not understand the solution. Short review: My program is a GUI program in C# which suppose to run on two computers in local network and always synchronize, therefore, you receive some kind of shared space to work together with your friend from another computer. My cryptography is in a singleton instance which is responsible to the communication of my program. Here are the functions that Encrypt and Decrypt the data, they receive that data as text or bytes array and return it as byte array or text (depends on which one, 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.
}
}
The “Padding is invalid and cannot be removed” is shown on the console when I run this program, I know that the encryption is successful (I see it encrypted when it moves through the server) but when I try to Decrypt it writes the exception.
Upvotes: 0
Views: 2276
Reputation: 127593
Doing cs.Read(x, 0, x.Length)
is not the correct thing to do. You need to put the byte array in the memory stream's constructor then read out the data to a string using cs.Read(
in a loop till you have all the bytes 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();
}
or by wrapping it in a StreamReader
to make the code easier.
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();
}
}
}
(Note: I would reccomend changing your encrypting code to be a StreamWriter
for similar reasons. Also ASCII is a bad encoding choice, it only supports 7 bit charactors. Use Enocding.UTF8
instead, it is a much more common encoding and will still take up the same amount of space if you don't use any special characters but you don't loose the characters if you do end up having them in your string like you will with ASCII encoding)
UPDATE: there is a 2nd problem with your code. You never set desObj.IV
on the sending or receiving side. It will use a randomly generated IV if you do not explicitly assign one. Fixing the MemoryStream error and the IV error will make the code work.
Here is a full example you can even 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();
}
}
}
}
Upvotes: 2