Problem Updating to .Net 6 – Encrypting String

The reason is this breaking change:

DeflateStream, GZipStream, and CryptoStream diverged from typical
Stream.Read and Stream.ReadAsync behavior in two ways:

They didn’t complete the read operation until either the buffer passed
to the read operation was completely filled or the end of the stream
was reached.

And the new behaviour is:

Starting in .NET 6, when Stream.Read or Stream.ReadAsync is called on
one of the affected stream types with a buffer of length N, the
operation completes when:

At least one byte has been read from the stream, or The underlying
stream they wrap returns 0 from a call to its read, indicating no more
data is available.

In your case you are affected because of this code in Decrypt method:

using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
    var plainTextBytes = new byte[cipherTextBytes.Length];
    var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
    memoryStream.Close();
    cryptoStream.Close();
    return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}

You do not check how much bytes Read actually read and whether it read them all. You could get away with this in previous versions of .NET because as mentioned CryptoStream behaviour was different from other streams, and because your buffer length is enough to hold all data. However, this is no longer the case and you need to check it as you would do for other streams. Or even better – just use CopyTo:

using (var plainTextStream = new MemoryStream())
{
    cryptoStream.CopyTo(plainTextStream);
    var plainTextBytes = plainTextStream.ToArray();
    return Encoding.UTF8.GetString(plainTextBytes, 0, plainTextBytes.Length);
} 

Or even better as another answer suggests, since you decrypt UTF8 text:

using (var plainTextReader = new StreamReader(cryptoStream))
{
    return plainTextReader.ReadToEnd();
}  

Leave a Comment