I am trying to encrypt/decrypt bytes – I have done a lot of reading about the Key and IV for the AES algorithm using the AESManaged class in System.Security.Cryptography. I read James Johnson's answer to the following question http://www.techques.com/question/1-7025135/My-Length-of-the-data-to-decrypt-is-invalid-error where he suggests that you use a random IV in the encryption routine and prepend the IV to the encrypted message. The decrypt function strips off the random IV from the beginning of the encrypted message to initialize the decryption class and then decrypts the rest of the bytes. I have attempted to do this in the following code. But I keep getting the "Length of the data to decrypt is invalid." error message when I attempt the decrypt after the encryption. Could someone possibly shed some light on what might be wrong.
USAGE: (streamToEncrypt/streamToDecrypt are System.IO.Stream)
using (var cryptoHelper = new AESHelper())
{
var encryptedBytes = cryptoHelper.Encrypt(AESHelper.StreamToByteArray(streamToEncrypt));
}
using (var cryptoHelper = new AESHelper())
{
var decryptedBytes = cryptoHelper.Decrypt(AESHelper.StreamToByteArray(streamToDecrypt));
}
public class AESHelper : IDisposable
{
public AesManaged AESManaged;
internal ICryptoTransform Encryptor { get; set; }
internal ICryptoTransform Decryptor { get; set; }
private const string KEY = "2428GD19569F9B2C2341839416C8E87G";
private static readonly byte[] Salt = Encoding.ASCII.GetBytes("?pt1$8f]l4g80");
private const Int32 ITERATIONS = 1042;
internal AESHelper()
{
AESManaged = new AesManaged();
AESManaged.BlockSize = AESManaged.LegalBlockSizes[0].MaxSize;
AESManaged.KeySize = AESManaged.LegalKeySizes[0].MaxSize;
AESManaged.Mode= CipherMode.CBC;
}
public void KeyGenerator()
{
var key = new Rfc2898DeriveBytes(KEY, Salt, ITERATIONS);
AESManaged.Key = key.GetBytes(AESManaged.KeySize / 8);
}
public byte[] Encrypt(byte[] input)
{
KeyGenerator();
var ms = new MemoryStream();
//Random IV
Encryptor = AESManaged.CreateEncryptor(AESManaged.Key, AESManaged.IV);
//Add the IV to the beginning of the memory stream
ms.Write(BitConverter.GetBytes(AESManaged.IV.Length), 0, sizeof(int));
ms.Write(AESManaged.IV, 0, AESManaged.IV.Length);
var cs = new CryptoStream(ms,
Encryptor, CryptoStreamMode.Write);
cs.Write(input, 0, input.Length);
cs.Close();
return ms.ToArray();
}
public byte[] Decrypt(byte[] input)
{
KeyGenerator();
// Get the initialization vector from the encrypted stream
var ms = new MemoryStream(input);
AESManaged.IV = ReadByteArray(ms);
Decryptor = AESManaged.CreateDecryptor(AESManaged.Key, AESManaged.IV);
var cs = new CryptoStream(ms,
Decryptor, CryptoStreamMode.Write);
cs.Write(input, 0, input.Length);
cs.Close();//Error occurs here
return ms.ToArray();
}
internal static byte[] ReadByteArray(Stream s)
{
var rawLength = new byte[sizeof(int)];
if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
{
throw new SystemException("Stream did not contain properly formatted byte array");
}
var buffer = new byte[16];
if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
{
throw new SystemException("Did not read byte array properly");
}
return buffer;
}
internal static byte[] StreamToByteArray(Stream inputStream)
{
if (!inputStream.CanRead)
{
throw new ArgumentException();
}
// This is optional
if (inputStream.CanSeek)
{
inputStream.Seek(0, SeekOrigin.Begin);
}
var output = new byte[inputStream.Length];
var bytesRead = inputStream.Read(output, 0, output.Length);
Debug.Assert(bytesRead == output.Length, "Bytes read from stream matches stream length");
return output;
}
public void Dispose()
{
if (AESManaged != null)
((IDisposable) AESManaged).Dispose();
}}
Many Thanks in advance
Best Answer
Probably you have solved this already but I'll just put my answer for others who faces similar issue.
Error occurs due to the additional information present in the
input
array. Inpublic byte[] Encrypt(byte[] input)
method you are writing IV length and IV before the ciphered data is written. Lines:In
public byte[] Decrypt(byte[] input)
method you are reading this information and using read IV as initialization vector for AES algorithm. All fine. Then you are constructingCryptoStream
withCryptoStreamMode.Write
and passingMemoryStream
objectms
which gets decrypted data. However the passedinput
array contains not only the encrypted message but also the IV that you wrote during the encryption process. That is why it fails to decrypt.What you need to do to overcome this is either extract only cipher data from the
input
array and pass it to:cs.Write(cipherData, 0, cipherData.Length);
or change mode intoCryptoStreamMode.Read
and usecs.Read(outputBuff, 0, outputBuff.Length);
.Also don't use the same
MemoryStream
object to read and write to because you'll have some garbage in it afterCryptoStream
will write in it.