I am currently upgrading from .NET Core 3.1 to .NET 6 and I came accross a breaking change or an odd behavior.
This is my code, that works perfectly in .NET Core 3.1:
public static byte[] Decrypt<SymmetricAlgorithmType>(byte[] securedData, SymmetricEncryptionOptions symetricEncryptionOptions)
where SymmetricAlgorithmType : SymmetricAlgorithm, new()
{
SymmetricAlgorithm symetric = new SymmetricAlgorithmType
{
Key = symetricEncryptionOptions.Key,
IV = symetricEncryptionOptions.InitializationVector
};
byte[] bytes = new byte[securedData.Length];
MemoryStream? memoryStream = new MemoryStream(securedData);
CryptoStream encryptionStream = new CryptoStream(memoryStream, symetric.CreateDecryptor(), CryptoStreamMode.Read);
int realBytes = encryptionStream.Read(bytes, 0, bytes.Length);
encryptionStream.Close();
byte[] realByteArray = new byte[realBytes];
Array.Copy(bytes, realByteArray, realBytes);
return realByteArray;
}
When running my unit test, with some sample data, the size of securedData
is 32. The number of bytes actually read is 23 (also seen in the realBytes
variable) and those are put into realByteArray
of the correct size.
Running the exact same code in .NET 6, I now get 16 instead of 23, which crops the content.
So I thought, I rewrite the code to create a while loop that breaks until 0 bytes are read:
MemoryStream? memoryStream = new MemoryStream(securedData);
CryptoStream encryptionStream = new CryptoStream(memoryStream, symetric.CreateDecryptor(), CryptoStreamMode.Read);
byte[] buffer = new byte[securedData.Length];
int totalRead = 0;
while (totalRead < buffer.Length)
{
int bytesRead = encryptionStream.Read(buffer, totalRead, 16);
if (bytesRead == 0)
{
break;
}
totalRead = bytesRead;
}
return buffer;
So on the first run, I get 16 bytes, the second run returns 7 bytes, but as soon as the loop runs the third time, I get the exception:
System.ArgumentOutOfRangeException: 'Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. (Parameter 'count')'
The code seen above is according to the following article, where the breaking change in .NET 6 is mentioned: https://docs.microsoft.com/en-US/dotnet/core/compatibility/core-libraries/6.0/partial-byte-reads-in-streams
The only workaround I have so far is to read it byte by byte. So the same code, as seen above, but using: 1
instead of 16
: encryptionStream.Read(buffer, totalRead, 1);
Note that I use a System.Security.Cryptography.RijndaelManaged
as SymmetricAlgorithm
with a 16 size byte array as key and also 16 size byte array as IV and the CryptoStream
does not support Seek
or Slice
.
So I am wondering if this is a bug in .NET 6 or I am doing something totally wrong now, but was okay in .NET Core 3?
CodePudding user response:
The error is yours.
The number of bytes returned from any Read
call is an implementation detail - it just has to be something or nothing (EOF). So the number changing: is fine. The problem, however, is here:
int bytesRead = encryptionStream.Read(buffer, totalRead, 16);
The parameters still need to be valid, which they aren't as you approach the end of the buffer. Consider instead:
int bytesRead = encryptionStream.Read(buffer, totalRead, buffer.Length - totalRead);
Now it always says "as much as you can, filling the buffer", rather than "read up to 16 bytes, regardless of how much space is left in the buffer".