I'm writing a simple library for encryption/decryption, using the Framework's objects. The methods are as follows:
public static byte[] Encrypt(byte[] key, byte[] vector, byte[] input)
{
if(key.Length == 0)
throw new ArgumentException("Cannot encrypt with empty key");
if (vector.Length == 0)
throw new ArgumentException("Cannot encrypt with empty vector");
if (input.Length == 0)
throw new ArgumentException("Cannot encrypt empty input");
var unencryptedBytes = input;
using(AesCryptoServiceProvider aes = new AesCryptoServiceProvider {Key = key, IV = vector})
using(ICryptoTransform encryptor = aes.CreateEncryptor())
using (MemoryStream ms = new MemoryStream())
using (CryptoStream writer = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
writer.Write(unencryptedBytes, 0, unencryptedBytes.Length);
writer.FlushFinalBlock();
var byteArray = ms.ToArray();
if(byteArray.Length == 0)
throw new Exception("Attempted to encrypt but encryption resulted in a byte array of 0 length.");
return byteArray;
}
}
public static byte[] Decrypt(byte[] key, byte[] vector, byte[] encrypted)
{
if (key.Length == 0)
throw new ArgumentException("Cannot encrypt with empty key");
if (vector.Length == 0)
throw new ArgumentException("Cannot encrypt with empty vector");
if (encrypted == null || encrypted.Length == 0)
throw new ArgumentException("Cannot decrypt empty or null byte array");
byte[] unencrypted;
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider { Key = key, IV = vector })
using (ICryptoTransform decryptor = aes.CreateDecryptor(key, vector))
using (MemoryStream ms = new MemoryStream(encrypted))
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
cs.Read(encrypted, 0, encrypted.Length);
unencrypted = ms.ToArray();
}
return unencrypted;
}
Feeding it in an encoded string, for example, as follows:
var expected = "This is an example message";
var messageBytes = Encoding.UTF8.GetBytes(expected);
Goes in as (in this example) 26 bytes. After encryption it leaves as 32 bytes, and when this is translated back as a string it has random characters appended at the end but is otherwise perfectly valid, as follows:
"This is an example message�(4���"
Generating a new vector/key changes the random characters that are appended to the end.
How can I eliminate this extra-byte behavior?
EDIT
Included a requested unit test.
[TestMethod]
public void CanEncryptAndDecryptByteArray()
{
var expected = "This is an example message";
var messageBytes = Encoding.UTF8.GetBytes(expected);
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.GenerateIV();
aes.GenerateKey();
var byteKey = Convert.ToBase64String(aes.Key);
var vectorKey = Convert.ToBase64String(aes.IV);
var key = Convert.FromBase64String("Qcf+3VzYNAfPUCfBO/ePSxCLBLItkVfk8ajK86KYebs=");
var vector = Convert.FromBase64String("aJNKWP7M2D44jilby6BzGg==");
var encrypted = EncryptionManager.Encrypt(key, vector, messageBytes);
var decrypted = EncryptionManager.Decrypt(key, vector, encrypted);
var actual = Encoding.UTF8.GetString(decrypted);
Assert.AreEqual(expected, actual);
}
The junk-bytes are because we're reading and writing to the same array.
new MemoryStream(encrypted)) Is telling the crypt to read from encrypted.
cs.Read(encrypted, 0, encrypted.Length); Is telling it to write to encrypted.
The solution:
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider { Key = key, IV = vector })
using (ICryptoTransform decryptor = aes.CreateDecryptor(key, vector))
using (MemoryStream ms = new MemoryStream(encrypted))
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
var decrypted = new byte[encrypted.Length];
var bytesRead = cs.Read(decrypted, 0, encrypted.Length);
return decrypted.Take(bytesRead).ToArray();
}
Note that we take bytesRead from the array, rather than the full array, as we would otherwise end up with null bytes (which fail a string comparison but appear identical in editors)
AES encryption does the encryption in 16, 24 or 32 byte (or 128, 192 or 256 bits) blocks. So the data you get back is just random data packed onto the end.
You'll have to prepend your data with a couple of bytes to represent the actual legnth:
var length = new byte [] { (byte) ((unencryptedBytes.Length >> 8) & 0xff) ,
(byte) (unencryptedBytes.Length & 0xff);
writer.Write(length, 0, length.Length);
writer.Write(unencryptedBytes, 0, unencryptedBytes.Length);
writer.FlushFinalBlock();
and then in the reader retrieve the length:
unencrypted = ms.ToArray();
int length = ((int) unencrypted[0] << 8) + unencrypted[1];
var result = new byte[length];
unencrypted.CopyTo(result, 2, 0, length);
return result;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With