This is a really common exception, but obviously none of the solutions I've found have resolved my issue.
I have an Encrypt and a Decrypt method; I encrypt a string and write it to a file, then read the string from the file and decrypt it (in theory). In reality, I get a
CryptographicException: Length of the data to decrypt is invalid
on the decryption side of the process.
Here's the Main()
method that does all the work:
public static void Main()
{
var filename = "test.encrypted";
var plainText = "Lorem Ipsum is simply dummy text of the printing and typesetting industry.";
string password = "A better password than this";
string salt = "Sodium Chloride";
var padding = PaddingMode.Zeros; // I have tried every padding mode to no avail
var encrypted = Encrypt<AesManaged>(plainText, password, salt, padding);
File.WriteAllBytes(filename, encrypted);
var fileBytes = File.ReadAllBytes(filename);
var decrypted = Decrypt<AesManaged>(fileBytes, password, salt, padding);
Console.ReadLine();
}
The encrypt side:
static byte[] Encrypt<T>(string plainText, string password, string salt, PaddingMode padding)
where T : SymmetricAlgorithm, new()
{
var saltBytes = Encoding.Unicode.GetBytes(salt);
var derivedBytes = new Rfc2898DeriveBytes(password, saltBytes);
using (var algorithm = new T())
{
algorithm.Padding = padding;
var key = derivedBytes.GetBytes(algorithm.KeySize >> 3);
byte[] iv = new byte[algorithm.BlockSize >> 3];
RNGCryptoServiceProvider.Create().GetNonZeroBytes(iv);
var transform = algorithm.CreateEncryptor(key, iv);
using (MemoryStream buffer = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))
using (StreamWriter writer = new StreamWriter(cryptoStream, Encoding.Unicode))
{
writer.Write(plainText);
writer.Flush();
// cryptoStream.FlushFinalBlock() is called after writer.Flush()
// or as part of the Dispose().
// Calling it here causes a "you can't call that twice" exception.
// prepend IV to the data
return iv.Concat(buffer.ToArray()).ToArray();
}
}
}
... and the flip side
static byte[] Decrypt<T>(byte[] encryptedData, string password, string salt, PaddingMode padding)
where T : SymmetricAlgorithm, new()
{
var saltBytes = Encoding.Unicode.GetBytes(salt);
var derivedBytes = new Rfc2898DeriveBytes(password, saltBytes);
using (var algorithm = new T())
{
algorithm.Padding = padding;
var key = derivedBytes.GetBytes(algorithm.KeySize >> 3);
//IV is at the beginning of the data
var iv = encryptedData.Take(algorithm.BlockSize >> 3).ToArray();
encryptedData = encryptedData.Skip(iv.Length).ToArray();
var transform = algorithm.CreateDecryptor(key, iv);
using (MemoryStream buffer = new MemoryStream())
using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))
using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
{
writer.Write(encryptedData);
writer.Flush();
return buffer.ToArray();
}
}
}