0
votes

I'm trying to encrypt/decrypt a string with AES, using streams. I'm using the following code for encryption:

var provider = Aes.Create();
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
using var encryptor = provider.CreateEncryptor();
using var memoryStream = new MemoryStream();
using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
using var streamWriter = new StreamWriter(cryptoStream, Encoding.UTF8);
streamWriter.Write(plainText);
cryptoStream.FlushFinalBlock();
var cipher = memoryStream.ToArray();

This successfully produces a byte array, though no matter the plaintext length, the cipher is always 16 bytes. From my understanding, with a block size of 16, a plaintext string with a length of 16 or more should result in a cipher that is larger than 16 bytes. Also, even for plaintext that is less than 16 bytes, decryption always results in an empty string.

var provider = Aes.Create();
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
using var decryptor = _provider.CreateDecryptor(key, iv);
using var memoryStream = new MemoryStream(cipher);
using var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
using var streamReader = new StreamReader(cryptoStream, Encoding.UTF8);
var plainText = streamReader.ReadToEnd();

My code is based on this sample in the Microsoft docs, though I'm calling cryptoStream.FlushFinalBlock(), after writing to the stream, although this isn't working as desired.

1
Disregard my last comment. This code requires C# 8 to compile. - user47589
Can you try using for the CryptoStream and StreamWriter instance, not call FlushFinalBlock and only call ToArray after you're out of the using block of the CryptoStream & StreamWriter? Because currently you're not closing any of the streams, and that's bad stream handling. In other words it is the part of the demo that you didn't copy. - Maarten Bodewes
@MaartenBodewes You were correct! Closing the CryptoStream solved the issue and meant that I didn't need to call FlushFinalBlock. Can you post this as an answer so I can accept it? - drmathias
OK, I've created an answer, please indicate if anything is missing from it or if anything is not easy to read. - Maarten Bodewes

1 Answers

0
votes

Calling FlushFinalBlock is not really necessary if you correctly close the stream. It might be useful if you want to write the last block (including padding) without closing it.

However, using the generic streaming API, preferably with the using statement and closing the stream should write any bytes left in the buffer + any padding that could be required.

Of course you should include any stream that writes to the CryptoStream in that using statement, otherwise they may have leftover data. Of course the receiving stream should only be closed after the data has been retrieved, for instance in case a MemoryStream is used.