I'm trying to encrypt binary data with AES in .Net 6.0. Unfortunately, the decryption does not bring back the original data:
public static byte[] Encrypt(byte[] plainBytes)
{
using (var aes = System.Security.Cryptography.Aes.Create())
{
byte[] cipherBytes;
using (MemoryStream cipherStream = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateEncryptor(aes.Key, aes.IV), CryptoStreamMode.Write))
{
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
cryptoStream.FlushFinalBlock();
cipherStream.Seek(0, SeekOrigin.Begin);
cipherBytes = new byte[cipherStream.Length];
cipherStream.Read(cipherBytes, 0, cipherBytes.Length);
}
byte[] wrongPlainBytes;
using (MemoryStream cipherStream = new MemoryStream(cipherBytes))
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateDecryptor(aes.Key, aes.IV), CryptoStreamMode.Read))
{
wrongPlainBytes = cipherStream.ToArray();
}
bool shouldBeTrue = plainBytes.Equals(wrongPlainBytes);
return cipherBytes;
}
}
After executing this code, shouldBeTrue
should be true
, but it's false
. Also, plainBytes.Length
is different from wrongPlainBytes.Length
.
What do I wrong while encryption / decryption?
CodePudding user response:
Try to avoid direct Read
on streams where possible - this operation is not guaranteed to read the amount of bytes you requested, so when you do use it - you need to check the return value (which you ignore) to figure out actual number of bytes that were read, and then act accordingly (do more reads if that amount is less than what you need).
There are several issues in your code, which you can avoid this way:
public static byte[] Encrypt(byte[] plainBytes)
{
using (var aes = System.Security.Cryptography.Aes.Create())
{
byte[] cipherBytes;
using (MemoryStream cipherStream = new MemoryStream()) {
// use leaveOpen parameter here, that way it will NOT dispose cipherStream when closing cryptoStream
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateEncryptor(aes.Key, aes.IV), CryptoStreamMode.Write, true)) {
// just write
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
}
// now, cryptoStream is disposed, so we can be sure all data is propertly written to the cipherStream
// no need to flush or anything
// do not manage buffer yourself, use ToArray
cipherBytes = cipherStream.ToArray();
}
byte[] wrongPlainBytes;
using (MemoryStream cipherStream = new MemoryStream(cipherBytes)) {
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateDecryptor(aes.Key, aes.IV), CryptoStreamMode.Read)) {
using (var output = new MemoryStream()) {
// same story here, avoid direct reads, use CopyTo, it will copy all data, decrypted, to memory stream
cryptoStream.CopyTo(output);
// again, just ToArray to avoid manage buffer directly
wrongPlainBytes = output.ToArray();
}
}
}
bool shouldBeTrue = plainBytes.SequenceEqual(wrongPlainBytes);
return cipherBytes;
}
}
CodePudding user response:
public static byte[] Encrypt( byte[] plainBytes )
{
using ( var aes = System.Security.Cryptography.Aes.Create() )
{
byte[] cipherBytes;
using ( MemoryStream cipherStream = new MemoryStream() )
using ( CryptoStream cryptoStream = new CryptoStream( cipherStream, aes.CreateEncryptor( aes.Key, aes.IV ), CryptoStreamMode.Write ) )
{
cryptoStream.Write( plainBytes, 0, plainBytes.Length );
cryptoStream.FlushFinalBlock();
cipherBytes = cipherStream.ToArray();
}
byte[] wrongPlainBytes;
using ( MemoryStream cipherStream = new MemoryStream() )
using ( CryptoStream cryptoStream = new CryptoStream( cipherStream, aes.CreateDecryptor( aes.Key, aes.IV ), CryptoStreamMode.Write ) )
{
cryptoStream.Write( cipherBytes, 0, cipherBytes.Length );
cryptoStream.FlushFinalBlock();
wrongPlainBytes = cipherStream.ToArray();
}
bool shouldBeTrue = plainBytes.SequenceEqual( wrongPlainBytes );
return cipherBytes;
}
}