C# CryptoStream.Close() writes additional block into an attached MemoryStream - c#

I have an encryption function based on the standard library AES with the block size of 16 bytes.
I'm encrypting a single block of size 16 bytes with a 16 byte key, using CryptoStream and MemoryStream.
If I Write and Flush, the result has 16 bytes. If I Write, Flush, and Close, it's 32 bytes.
Consider the example with the following encryption function:
public static byte[] EncryptBytes(byte[] data, string password, bool close)
{
var myAes = Aes.Create();
myAes.BlockSize = 128;
myAes.IV = new byte[16];
myAes.Key = Encoding.ASCII.GetBytes(password);
var encryptor = myAes.CreateEncryptor(myAes.Key, myAes.IV);
using var memoryStream = new MemoryStream();
using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.Flush();
if (close)
{
cryptoStream.Close(); // The offending line
}
byte[] cipher = memoryStream.ToArray();
return cipher;
}
Then running the following program:
string key = "0123456789ABCDEF";
byte[] data = key.Select(c => (byte) c).ToArray();
string a = EncryptBytes(data, key, true).Aggregate("", (s, b) => s + b.ToString("X2"));
string b = EncryptBytes(data, key, false).Aggregate("", (s, b) => s + b.ToString("X2"));
Console.WriteLine($"A: {a}");
Console.WriteLine($"B: {b}");
Results in:
A: 8D835D0CFFD8BAD585FE8B4294C42188786F695CF204DC5AA109BA26653DFD97
B: 8D835D0CFFD8BAD585FE8B4294C42188
It's clear that A is the same as B for the first 16 bytes, but I would expect the B result and do not understand why the two are different.

Related

BouncyCastle decrypt output size incorrect

I am using BouncyCastle.NetCore. At the moment of decryption, it seems the GetOutputSize call returns a larger than the actual required length for the output byte array, with the extra bytes having value '0x00'.
Before converting it back to objects or strings, I hence need to recreate the byte array with the actual length that I retrieve from the ProcessBytes and DoFinal calls.
Should I be using a different call to get the correct number of bytes beforehand or how should I go about this?
public AesEncryptor()
{
// AES - CBC - with default PKCS5/PKCS7 scheme
_encryptionCipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new AesEngine()));
_decryptionCipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new AesEngine()));
}
public byte[] Encrypt(byte[] iv, byte[] byteArrayToEncrypt)
{
ParametersWithIV keyParamWithIV = new ParametersWithIV(_keyParam, iv, 0, iv.Length);
byte[] encryptedBytes;
lock (_encryptionLock)
{
_encryptionCipher.Init(true, keyParamWithIV);
encryptedBytes = new byte[_encryptionCipher.GetOutputSize(byteArrayToEncrypt.Length)];
int length = _encryptionCipher.ProcessBytes(byteArrayToEncrypt, encryptedBytes, 0);
_encryptionCipher.DoFinal(encryptedBytes, length);
}
return encryptedBytes;
}
public byte[] Decrypt(byte[] iv, byte[] byteArrayToDecrypt)
{
ParametersWithIV keyParamWithIV = new ParametersWithIV(_keyParam, iv, 0, iv.Length);
byte[] decryptedBytesReworked;
lock (_decryptionLock)
{
_decryptionCipher.Init(false, keyParamWithIV);
var decryptedBytes = new byte[_decryptionCipher.GetOutputSize(byteArrayToDecrypt.Length)];
int length = _decryptionCipher.ProcessBytes(byteArrayToDecrypt, decryptedBytes, 0);
int newLength = _decryptionCipher.DoFinal(decryptedBytes, length); //Do the final block
// TODO - incorrect initial byte array length
decryptedBytesReworked = new byte[length + newLength];
Array.Copy(decryptedBytes, decryptedBytesReworked, decryptedBytesReworked.Length);
}
return decryptedBytesReworked;
}
Cheers.

C# Aes byte[] Encrypt/Decrypt : not a complete block

I get error "the input data is not a complete block", I dont know if my code is wrong or something is missing. i try to encrypt/decrypt bytes with same lenght.
=byte[] plain => MyEnc(plain) => byte[] encrypted => MyDec(encrypt) => byte[] plain
Plain and encrypted have the same length.
This is my encryption code:
public static byte[] MyEnc(byte[] Input)
{
byte[] inputencdec = Input;
byte[] encrypted;
using (MemoryStream mstream = new MemoryStream())
{
using (AesCryptoServiceProvider encdec = new AesCryptoServiceProvider())
{
encdec.BlockSize = 128;
encdec.KeySize = 256;
encdec.Key = ASCIIEncoding.ASCII.GetBytes(Key);
encdec.IV = ASCIIEncoding.ASCII.GetBytes(IV);
ICryptoTransform icrypt = encdec.CreateEncryptor(encdec.Key, encdec.IV);
using (CryptoStream cryptoStream = new CryptoStream(mstream,
icrypt, CryptoStreamMode.Write))
{
cryptoStream.Write(inputencdec, 0, inputencdec.Length);
}
}
encrypted = mstream.ToArray();
}
return encrypted;
}
this is my decryption code:
public static byte[] MyDec(byte[] Input)
{
byte[] inputencdec = Input;
byte[] buffer = new byte[Input.Length];
int totalRead = 0;
byte[] plain;
MemoryStream plainStream = new MemoryStream();
using (MemoryStream mStream = new MemoryStream(inputencdec))
{
using (AesCryptoServiceProvider encdec = new AesCryptoServiceProvider())
{
encdec.BlockSize = 128;
encdec.KeySize = 256;
encdec.Key = ASCIIEncoding.ASCII.GetBytes(Key);
encdec.IV = ASCIIEncoding.ASCII.GetBytes(IV);
ICryptoTransform icrypt = encdec.CreateDecryptor(encdec.Key, encdec.IV);
using (CryptoStream cryptoStream = new CryptoStream(mStream, icrypt, CryptoStreamMode.Read))
{
while (true)
{
int read = cryptoStream.Read(buffer, 0, inputencdec.Length);
if (read == 0)
break;
else
plainStream.Write(buffer, totalRead, read);
totalRead += read;
}
}
}
plain = plainStream.ToArray();
}
return plain;
}
Plain and encrypted are not the same length for CBC mode encryption, which you are using. The plaintext needs to be padded before it can be encrypted, so the ciphertext size is always larger than the plaintext message for CBC (which is the default mode that the decryptor is using).
Streaming modes do not need to expand the ciphertext as they do not require padding. Unfortunately Microsoft opted not to include counter mode in .NET. You could use CFB mode.
You could also decide to implement counter mode using ECB if you require parallel encryption (in the comments below the question). Generally AES is so fast nowadays that parallelism is not required. Implementing a statically sized counter and CTR buffer should pale in comparison with creating the multithreaded code.
This is incorrect and may cause an issue:
int read = cryptoStream.Read(buffer, 0, inputencdec.Length);
you should of course put in buffer.length, not inputencdec.length. The use of a buffer is to store data in a buffer, by reading up to buffer.length bytes per loop iteration.
This is incorrect as well:
plainStream.Write(buffer, totalRead, read);
the problem is that totalRead should be the offset in the buffer, not the offset within the stream. You're reading into offset 0 of the buffer, so you should start writing from offset 0 as well.
You could also create a MemoryStream for the plaintext, wrap it with a CryptoStream using a decryptor, and then write the ciphertext all in one go. There is no need for the encryption / decryption to use a different scheme, as far as I can see. You seem to keep all the plaintext / ciphertext in memory anyway.
Notes:
for CBC mode the IV should be random; it is often prefixed to the ciphertext and removed and used by the decryptor;
keys and IV's should consist of random bytes; they should not be strings.

c# AES Decryption

I am working with SagePay Forms and currently converting the VB examples they have to c#. I have made good progress so the encryption part of my project works fine (SagePay can decrypt it).
The issue I am having is that when I attempt to decrypt the string, it turns to garbage. If anyone has done this before I would really appreciate some help with my decryption code. I have included the encryption code which works and the first two lines are the setup and call from another method.
I haven't added the VB code but if this is required I could add it. Didn't want a huge post if not required.
Utility Methods:
public string byteArrayToHexString(byte[] ba)
{
return BitConverter.ToString(ba).Replace("-", "");
}
public static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
Main Encryption Method with first couple of lines being the calling of it extracted from a larger method.
string crypt = "blahblahblah"
string EncryptAndEncode = "#" + byteArrayToHexString(aesEncrypt(crypt));
private byte[] aesEncrypt(string inputText)
{
RijndaelManaged AES = new RijndaelManaged();
//set the mode, padding and block size for the key
AES.Padding = PaddingMode.PKCS7;
AES.Mode = CipherMode.CBC;
AES.KeySize = 128;
AES.BlockSize = 128;
//convert key and plain text input into byte arrays
Byte[] keyAndIvBytes = UTF8Encoding.UTF8.GetBytes("tR7nR6wZHGjYMCuV");
Byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(inputText);//AbHLlc5uLone0D1q
//create streams and encryptor object
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, AES.CreateEncryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Write);
//perform encryption
cryptoStream.Write(inputBytes, 0, inputBytes.Length);
cryptoStream.FlushFinalBlock();
//get encrypted stream into byte array
Byte[] outBytes = memoryStream.ToArray();
//close streams
memoryStream.Close();
cryptoStream.Close();
AES.Clear();
return outBytes;
}
Decoding and Decrypting methods
public string DecodeAndDecrypt(string strIn)
{
//** HEX decoding then AES decryption, CBC blocking with PKCS5 padding - DEFAULT **
string DecodeAndDecrypt = aesDecrypt(StringToByteArray(strIn.Substring(1)));
return (DecodeAndDecrypt);
}
private string aesDecrypt(Byte[] inputBytes)
{
RijndaelManaged AES = new RijndaelManaged();
Byte[] keyAndIvBytes = UTF8Encoding.UTF8.GetBytes("tR7nR6wZHGjYMCuV");
Byte[] outputBytes = inputBytes;//Convert.FromBase64String(inputBytes);
//set the mode, padding and block size
AES.Padding = PaddingMode.PKCS7;
AES.Mode = CipherMode.CBC;
AES.KeySize = 128;
AES.BlockSize = 128;
//create streams and decryptor object
MemoryStream memoryStream = new MemoryStream(outputBytes);
CryptoStream cryptoStream = new CryptoStream(memoryStream, AES.CreateEncryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Read);
//perform decryption
cryptoStream.Read(outputBytes, 0, outputBytes.Length);
Trace.WriteLine(outputBytes);
//close streams
memoryStream.Close();
cryptoStream.Close();
AES.Clear();
//return System.Text.Encoding.UTF8.GetString(outputBytes);
string plainText = Encoding.UTF8.GetString(outputBytes,
0,
outputBytes.Length);
return plainText;
}
There are actually multiple problems with your code. First in your decrypt method you're creating an encryptor, that should be a decryptor. Secondly you're reading the entire block including the padding of your algorithm into the buffer when you do the decryption. Below is a class with the items fixed and should be returning the proper result. I do however suggest you find a better way of storing the key, putting in your code and generating it the way you'r edoing it is a no no. You should generate your key with an RNG (RNGCryptoServiceProvider) then hash it with a secure hashing algorithm such as SHA512, use that output for your key. You then need to find a good place to store it, I would look into encrypting your web.config file.
public static class EncryptionHelper
{
private static byte[] keyAndIvBytes;
static EncryptionHelper()
{
// You'll need a more secure way of storing this, I hope this isn't
// the real key
keyAndIvBytes = UTF8Encoding.UTF8.GetBytes("tR7nR6wZHGjYMCuV");
}
public static string ByteArrayToHexString(byte[] ba)
{
return BitConverter.ToString(ba).Replace("-", "");
}
public static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static string DecodeAndDecrypt(string cipherText)
{
string DecodeAndDecrypt = AesDecrypt(StringToByteArray(cipherText));
return (DecodeAndDecrypt);
}
public static string EncryptAndEncode(string plaintext)
{
return ByteArrayToHexString(AesEncrypt(plaintext));
}
public static string AesDecrypt(Byte[] inputBytes)
{
Byte[] outputBytes = inputBytes;
string plaintext = string.Empty;
using (MemoryStream memoryStream = new MemoryStream(outputBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateDecryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(cryptoStream))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
return plaintext;
}
public static byte[] AesEncrypt(string inputText)
{
byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(inputText);//AbHLlc5uLone0D1q
byte[] result = null;
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateEncryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Write))
{
cryptoStream.Write(inputBytes, 0, inputBytes.Length);
cryptoStream.FlushFinalBlock();
result = memoryStream.ToArray();
}
}
return result;
}
private static RijndaelManaged GetCryptoAlgorithm()
{
RijndaelManaged algorithm = new RijndaelManaged();
//set the mode, padding and block size
algorithm.Padding = PaddingMode.PKCS7;
algorithm.Mode = CipherMode.CBC;
algorithm.KeySize = 128;
algorithm.BlockSize = 128;
return algorithm;
}
}
Calling it is easy:
string crypt = "blahblahblah";
string EncryptAndEncode = EncryptionHelper.EncryptAndEncode(crypt);
Console.WriteLine(EncryptAndEncode);
Console.WriteLine(EncryptionHelper.DecodeAndDecrypt(EncryptAndEncode));
Console.ReadLine();

unable to open xml after decryption 1 byte to many?

I'm trying to encrypt XML, and after decryption I end up with 1 byte too many - probably because of padding. This is my code. How can I change this to make it work?
public byte[] encryptData(byte[] source,string key)
{
byte[] btKeyInBytes = UTF8Encoding.UTF8.GetBytes(key);
Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(key, btKeyInBytes);
AesManaged encryptor = new AesManaged();
encryptor.Padding = PaddingMode.Zeros;
using (MemoryStream encryptStream = new MemoryStream())
{
using (CryptoStream encStream = new CryptoStream(encryptStream, encryptor.CreateEncryptor(rfc.GetBytes(16), rfc.GetBytes(16)), CryptoStreamMode.Read))
{
//Read from the input stream, then encrypt and write to the output stream.
encStream.Write(source, 0, source.Length);
encStream.FlushFinalBlock();
encryptor.Clear();
}
encryptStream.Flush();
encryptedSource = encryptStream.ToArray();
}
return encryptedSource;
}
public byte[] decryptData(byte[] source, string key)
{
byte[] encryptedSource = null;
byte[] btKeyInBytes = UTF8Encoding.UTF8.GetBytes(key);
Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(key, btKeyInBytes);
AesManaged encryptor = new AesManaged();
encryptor.Padding = PaddingMode.Zeros;
using (MemoryStream encryptStream = new MemoryStream())
{
using (CryptoStream encStream = new CryptoStream(encryptStream, encryptor.CreateDecryptor(rfc.GetBytes(16), rfc.GetBytes(16)), CryptoStreamMode.Write))
{
//Read from the input stream, then encrypt and write to the output stream.
encStream.Write(source, 0, source.Length);
encStream.FlushFinalBlock();
encryptor.Clear();
}
encryptStream.Flush();
encryptedSource = encryptStream.ToArray();
}
return encryptedSource;
}
It seems that the padding gives me 1 extra byte during decryption
If your problem is padding, then PaddingMode.Zeros is about the worst choice, since zeros cannot always be reliably removed. Better to use PKCS7 padding.
It is also possible that the encoding of end-of-line has changed between systems. Some systems use a single byte while other systems use two bytes. You really need to look at exactly what is in the decrypted file, byte by byte, as #Rup suggests.
I got it!
Now let's try to explain.
Let's say I have a file of 927 bytes.
What I do is to read this file and split it in pieces of 656 bytes. This byte array of 656 bytes is being encrypted. The second array will be 271 bytes.
In every block for encryption I used padding. When decrypting, you will not be able to know in which block padding was used because every block now can be divided by 16 (because of the padding in the encryption). Basically I only want padding used for the last block(271).
so this is my new code:
public byte[] encryptData(byte[] source, string key, bool padding)
{
byte[] encryptedSource = null;
byte[] btKeyInBytes = UTF8Encoding.UTF8.GetBytes(key);
Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(key, btKeyInBytes);
AesManaged encryptor = new AesManaged();
//encryptor.Mode = CipherMode.CFB;
encryptor.KeySize = 128; // in bits
encryptor.Key = new byte[128 / 8]; // 16 bytes for 128 bit encryption
encryptor.IV = new byte[128 / 8];
if (padding) { encryptor.Padding = PaddingMode.PKCS7; }
else { encryptor.Padding = PaddingMode.None; }
using (MemoryStream encryptStream = new MemoryStream())
{
using (CryptoStream encStream =
new CryptoStream(encryptStream,
encryptor.CreateEncryptor(rfc.GetBytes(16),
rfc.GetBytes(16)),
CryptoStreamMode.Write))
{
//Read from the input stream, then encrypt and write to the output stream.
encStream.Write(source, 0, source.Length);
}
encryptStream.Flush();
encryptedSource = encryptStream.ToArray();
}
return encryptedSource;
}
public byte[] decryptData(byte[] source, string key,bool padding)
{
byte[] encryptedSource = null;
byte[] btKeyInBytes = UTF8Encoding.UTF8.GetBytes(key);
Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(key, btKeyInBytes);
AesManaged encryptor = new AesManaged();
encryptor.Key = new byte[128 / 8]; // 16 bytes for 128 bit encryption
encryptor.IV = new byte[128 / 8];
if (padding) { encryptor.Padding = PaddingMode.PKCS7; }
else { encryptor.Padding = PaddingMode.None; }
using (MemoryStream encryptStream = new MemoryStream())
{
using (CryptoStream encStream =
new CryptoStream(encryptStream,
encryptor.CreateDecryptor(rfc.GetBytes(16),
rfc.GetBytes(16)),
CryptoStreamMode.Write))
{
//Read from the input stream, then encrypt and write to the output stream.
encStream.Write(source, 0, source.Length);
}
encryptStream.Flush();
encryptedSource = encryptStream.ToArray();
}
return encryptedSource;
}
I hope this helps!

Decryption of file missing ~10 characters from ending

I've written Encryption/Decryption methods using the RC2CryptoServiceProvider in C# and for some reason, I cannot get my decryptor to decrypt the final few bytes. The file seems to just cut off. My encryption method looks like:
public static byte[] EncryptString(byte[] input, string password)
{
PasswordDeriveBytes pderiver = new PasswordDeriveBytes(password, null);
byte[] ivZeros = new byte[8];
byte[] pbeKey = pderiver.CryptDeriveKey("RC2", "MD5", 128, ivZeros);
RC2CryptoServiceProvider RC2 = new RC2CryptoServiceProvider();
byte[] IV = new byte[8];
ICryptoTransform encryptor = RC2.CreateEncryptor(pbeKey, IV);
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
csEncrypt.Write(input, 0, input.Length);
csEncrypt.FlushFinalBlock();
return msEncrypt.ToArray();
}
While my decryption looks like:
public static byte[] DecryptString(byte[] input, string password, int originalSize)
{
PasswordDeriveBytes pderiver = new PasswordDeriveBytes(password, null);
byte[] ivZeros = new byte[8];
byte[] pbeKey = pderiver.CryptDeriveKey("RC2", "MD5", 128, ivZeros);
RC2CryptoServiceProvider RC2 = new RC2CryptoServiceProvider();
byte[] IV = new byte[8];
ICryptoTransform decryptor = RC2.CreateDecryptor(pbeKey, IV);
MemoryStream msDecrypt = new MemoryStream();
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write);
csDecrypt.Write(input, 0, originalSize);
// csDecrypt.FlushFinalBlock();
char[] decrypted = new char[input.Length];
decrypted = System.Text.Encoding.UTF8.GetChars(msDecrypt.ToArray());
return msDecrypt.ToArray();
}
The char[] decrypted is returning the whole file decrypted, except the file ends with </LudoData> and when decrypting, I only get up to the first < character.
I have been playing with the lengths of things and nothing is changing anything. In my specific case, input is of length 11296, and originalSize is of size 11290. However, decrypted ends up being of size 11280 when decrypting. What gives!
Is there a reason that you have the Flush() commented out? Have you tried fully closing your streams?
Sigh, I fought this battle about a month ago and had a very similar issue, except I was experiencing TOO much on the end. ToArray was my solution.
You're doing some weird stuff here I'm not exactly sure of. You're using cryptostreams when you don't have to, you're keeping track of the original length for some weird reason and you're using deprecated classes. Your issue is probably a combination of padding, incorrect assumptions (evidenced by originalLength) and incorrect handling of streams (which can be tricky). Try this instead:
Encrypt:
var rij = RijndaelManaged.Create();
rij.Mode = CipherMode.CBC;
rij.BlockSize = 256;
rij.KeySize = 256;
rij.Padding = PaddingMode.ISO10126;
var pdb = new Rfc2898DeriveBytes(password,
Encoding.Default.GetBytes("lolwtfbbqsalt" + password));
var enc = rij.CreateEncryptor(pdb.GetBytes(rij.KeySize / 8),
pdb.GetBytes(rij.BlockSize / 8));
return enc.TransformFinalBlock(unencryptedBytes, 0, unencryptedBytes.Length);
Decrypt:
// throws a cryptographic exception if password is wrong
var rij = RijndaelManaged.Create();
rij.Mode = CipherMode.CBC;
rij.BlockSize = 256;
rij.KeySize = 256;
rij.Padding = PaddingMode.ISO10126;
var pdb = new Rfc2898DeriveBytes(password,
Encoding.Default.GetBytes("lolwtfbbqsalt" + password));
var dec = rij.CreateDecryptor(pdb.GetBytes(rij.KeySize / 8),
pdb.GetBytes(rij.BlockSize / 8));
return dec.TransformFinalBlock(encryptedBytes, 0,
encryptedBytes.Length);
Notice the only thing different within these two methods are CreateEncryptor/CreateDecryptor, so you can refactor out lots of duplication. Also notice I get in a byte array and get out a byte array without having to use any streams. Its also a bit more secure than RC2, and would be even more so if the salt were more random.

Categories