optimizing a decryption method in .net - c#

I need to decrypt large amounts of data quickly using the method below. Currently it takes about 0.3 ms to run with the ICryptoTransform provided. Can someone think any way to optimize it further ? The method is called multiple times in succession with different dataToDecrypt-value but with the same decryptor.
public byte[] DecryptUsingDecryptor(byte[] dataToDecrypt, ICryptoTransform decryptor)
{
byte[] decryptedData = null;
MemoryStream msDecrypt = new MemoryStream();
CryptoStream csDecrypt = new CryptoStream(msDecrypt,
decryptor,
CryptoStreamMode.Write);
csDecrypt.Write(dataToDecrypt, 0, dataToDecrypt.Length);
csDecrypt.FlushFinalBlock();
decryptedData = msDecrypt.ToArray();
csDecrypt.Close();
return decryptedData;
}

I don't really know if you would notice any performance improvement but if you are using the same decryptor couldn't you just re-use the same msDecrypt and csDecrypt by setting them as private fields?
public class Decrypter
{
private MemoryStream msDecrypt;
private CryptoStream csDecrypt;
public Decrypter(ICryptoTransform decryptor)
{
msDecrypt = new MemoryStream();
csDecrypt = new CryptoStream(msDecrypt,decryptor,CryptoStreamMode.Write);
}
public byte[] DecryptUsingDecryptor(byte[] dataToDecrypt)
{
byte[] decryptedData = null;
csDecrypt.Write(dataToDecrypt, 0, dataToDecrypt.Length);
csDecrypt.FlushFinalBlock();
decryptedData = msDecrypt.ToArray();
csDecrypt.Close();
return decryptedData;
}
}
As I said, I don't know if it would make any difference but I think that, at least, you won't be recreating your MemoryStream and CryptoStream every time.

Related

How to use CryptoStream with Span/ReadOnlySpan?

Most of the exemples use MemoryStream with CryptoStream to encrypt/decrypt with RijndaelManaged so I simplified it as much as I could and ended up with the following function to decrypt a buffer.
RijndaelManaged csp; //is initialized wih CBC/256bit key and padding
ICryptoTransform decryptor = csp.CreateDecryptor(csp.Key, csp.IV);
public void Decrypt(ref byte[] message, ref int len)
{
using (MemoryStream msDecrypt = new MemoryStream(message.AsSpan().Slice(0, len).ToArray())) //the buffer is larger than the actual data so we slice it to the actual size
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
len = csDecrypt.Read(message, 0, message.Length); //we write the encrypted data back into the buffer and set the length to the new size
}
}
}
It is working, but my application is now dealing with a lot of ReadOnlySpan and it feels weird to have to allocate a new buffer with "ToArray()" to be able to encrypt/decrypt them as most Streams are now capable of using Span in their Read and Write methods.
public void Decrypt(ref Span<byte> output, ref ReadOnlySpan<byte> input)
{
using (MemoryStream msDecrypt = new MemoryStream(input)) //This is not possible
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
csDecrypt.Read(output);
}
}
}
Is there a way similar to the above(even if it requires more steps) that allow the use of CryptoStream without allocating an additional buffer?

AES encryption in C# strange output

I implement the following method:
public static byte[] AESDecrypt(byte[] data, ICryptoTransform transform)
{
using (MemoryStream stream = new MemoryStream(data))
using (CryptoStream cstream = new CryptoStream(stream, transform, CryptoStreamMode.Read))
using (MemoryStream output = new MemoryStream())
{
byte[] buffer = new byte[4000];
int r;
while ((r = cstream.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, r);
}
stream.Close();
return output.ToArray();
}
}
I am using this method to decrypt a sequence of 16 bytes blocks, the transform parameter is initialized once at the beginning:
AesCryptoServiceProvider provider = new AesCryptoServiceProvider();
provider.Mode = CipherMode.ECB;
provider.KeySize = 128;
provider.BlockSize = 128;
provider.Key = key;
provider.Padding = PaddingMode.PKCS7;
transform = provider.CreateDecryptor();
My problem is that suddenly the method starts to produce strange output, 16 bytes block is decrypted to 27 bytes !!!!, sometimes 16 bytes are decrypted wrongly to 16 bytes, however when I restart the application the same data produce correct result, does the transform hold any state that makes this happen? what wrong thing I did that makes 16 bytes block decrypted to 27 bytes.
Any help is appreciated
`Edit:
Can someone confirm it is the same bug:
Reuse ICryptoTransform objects
Edit 2:
Something to add to the correct answer:
It seems ICryptoTransform is not thread safe, so calling the above method from two threads simultaneously may cause trouble, I solved it by creating ICrypteTransform object for each thread that is using the method
You are closing stream when you meant to close cstream.
Since you don't close cstream before reading out the data, TransformFinalBlock is never called.
You'd be better off using Stream.CopyTo, and making your output stream have a clearer longer-lifetime than the CryptoStream.
public static byte[] AESDecrypt(byte[] data, ICryptoTransform transform)
{
using (MemoryStream output = new MemoryStream())
{
using (MemoryStream stream = new MemoryStream(data))
using (CryptoStream cstream = new CryptoStream(stream, transform, CryptoStreamMode.Read))
{
cstream.CopyTo(output);
}
return output.ToArray();
}
}

Encrypting and Decrypting a file with AES generates a broken file

I'm trying to encrypt and decrypt a file using AES. The problem that I have is that when the file gets decrypted, it is broken and you can't open it. The original file has a length of 81.970 bytes and the decrypted file has a length of 81.984 bytes...so there are 14 bytes added for some reason. The problem could be in the way the file gets encrypted but I don't know what I'm doing wrong.
What am I missing here? Could it be the way I'm processing the password, the iv and the padding?
Thanks for your time!
This is the code I use to encrypt:
private AesManaged aesManaged;
private string filePathToEncrypt;
public Encrypt(AesManaged aesManaged, string filePathToEncrypt)
{
this.aesManaged = aesManaged;
this.filePathToEncrypt = filePathToEncrypt;
}
public void DoEncryption()
{
byte[] cipherTextBytes;
byte[] textBytes = File.ReadAllBytes(this.filePathToEncrypt);
using(ICryptoTransform encryptor = aesManaged.CreateEncryptor(aesManaged.Key, aesManaged.IV))
using (MemoryStream ms = new MemoryStream())
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(textBytes, 0, textBytes.Length);
cs.FlushFinalBlock();
cipherTextBytes = ms.ToArray();
}
File.WriteAllBytes("EncryptedFile.aes", cipherTextBytes);
}
This is the code I use to decrypt:
private AesManaged aesManaged;
private string filePathToDecrypt;
public Decrypt(AesManaged aesManaged, string filePathToDecrypt)
{
this.aesManaged = aesManaged;
this.filePathToDecrypt = filePathToDecrypt;
}
public void DoDecrypt()
{
byte[] cypherBytes = File.ReadAllBytes(this.filePathToDecrypt);
byte[] clearBytes = new byte[cypherBytes.Length];
ICryptoTransform encryptor = aesManaged.CreateDecryptor(aesManaged.Key, aesManaged.IV);
using (MemoryStream ms = new MemoryStream(cypherBytes))
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Read))
{
cs.Read(clearBytes, 0, clearBytes.Length);
clearBytes = ms.ToArray();
}
File.WriteAllBytes("DecryptedFile.gif", clearBytes);
}
And here is how I call the functions:
string filePathToEncrypt = "dilbert.gif";
string filePathToDecrypt = "EncryptedFile.aes";
string password = "Password";
string passwordSalt = "PasswordSalt";
Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(password, Encoding.ASCII.GetBytes(passwordSalt));
var aesManaged = new AesManaged
{
Key = deriveBytes.GetBytes(128 / 8),
IV = deriveBytes.GetBytes(16),
Padding = PaddingMode.PKCS7
};
Console.WriteLine("Encrypting File...");
var encryptor = new Encrypt(aesManaged, filePathToEncrypt);
encryptor.DoEncryption();
Thread.Sleep(300);
Console.WriteLine("Decrypting File...");
var decryptor = new Decrypt(aesManaged, filePathToDecrypt);
decryptor.DoDecrypt();
Thread.Sleep(300);
Try with:
public void DoEncryption()
{
byte[] cipherBytes;
byte[] textBytes = File.ReadAllBytes(this.filePathToEncrypt);
using (ICryptoTransform encryptor = aesManaged.CreateEncryptor(aesManaged.Key, aesManaged.IV))
using (MemoryStream input = new MemoryStream(textBytes))
using (MemoryStream output = new MemoryStream())
using (CryptoStream cs = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
{
input.CopyTo(cs);
cs.FlushFinalBlock();
cipherBytes = output.ToArray();
}
File.WriteAllBytes("EncryptedFile.aes", cipherBytes);
}
and
public void DoDecrypt()
{
byte[] cypherBytes = File.ReadAllBytes(this.filePathToDecrypt);
byte[] textBytes;
using (ICryptoTransform decryptor = aesManaged.CreateDecryptor(aesManaged.Key, aesManaged.IV))
using (MemoryStream input = new MemoryStream(cypherBytes))
using (MemoryStream output = new MemoryStream())
using (CryptoStream cs = new CryptoStream(input, decryptor, CryptoStreamMode.Read))
{
cs.CopyTo(output);
textBytes = output.ToArray();
}
File.WriteAllBytes("DecryptedFile.gif", textBytes);
}
Note that the code could be modified to not use temporary byte[] and read/write directly to input/output streams.
In general you can't desume the length of the plaintext from the length of the cyphertext, so this line:
new byte[cypherBytes.Length]
was totally wrong.
And please, don't use Encoding.ASCII in 2016. It is so like previous century. Use Encoding.UTF8 to support non-english characters.
The answer may be very simple. I don't see where do u try to choose a cipher mode, so by default it probably takes CBC, as IV was inited. Then, 81.970 are padded by 14 bytes, to be divisible by 32. So when it happens, the memory you allocated was just 81.970, so the padding bytes doesn't write correctly, cause of some sort of memory leak, and when decrypt is started, unpadding doesn't work correctly.

How to return byte[] when decrypt using CryptoStream (DESCryptoServiceProvider)

This is a beginner question,
Every time I search on the internet, decrypt with DESCryptoServiceProvider function always returning a string.
How can we get byte[] for the return?
This is the code. Thank you for any help.
DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider();
cryptoProvider.Padding = PaddingMode.None;
cryptoProvider.Mode = CipherMode.CBC;
MemoryStream memoryStream = new MemoryStream(value);
CryptoStream cryptoStream = new CryptoStream(memoryStream,
cryptoProvider.CreateDecryptor(password, initVector), CryptoStreamMode.Read);
StreamReader reader = new StreamReader(cryptoStream);
return reader.ReadToEnd();
//how to return byte[];
I had this problem too, and I created a class with some functions to help me with this issues.
The function to perform the cryptography is:
private byte[] PerformCryptography(ICryptoTransform cryptoTransform, byte[] data)
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return memoryStream.ToArray();
}
}
}
The ICryptoTransform is either a spezific encryptor or decryptor.
This Method works for symmetric altorithm's
Just for example, the methods for encryption and decryption look like:
public byte[] Encrypt(byte[] data)
{
if (CanPerformCryptography(data))
{
using (var encryptor = _algorithm.CreateEncryptor(_key, _iv))
{
return PerformCryptography(encryptor, data);
}
}
return data;
}
public byte[] Decrypt(byte[] data)
{
if (CanPerformCryptography(data))
{
using (var decryptor = _algorithm.CreateDecryptor(_key, _iv))
{
return PerformCryptography(decryptor, data);
}
}
return data;
}

Reading from a CryptoStream results in a padding error

I'm writing an Aes decryption method and currently, I'm stuck on trying to read all the contents inside my CryptoStream and put it all into a byte[]. This is what I have for decryption:
public byte[] GetDecrypted()
{
byte[] toReturn;
using (Aes dec = Aes.Create())
{
dec.Key = Key;
dec.IV = IV;
ICryptoTransform cryptoTransform = dec.CreateDecryptor(dec.Key, dec.IV);
using (MemoryStream ms = new MemoryStream(Data))
{
using (CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Read))
{
using (MemoryStream decMs = new MemoryStream())
{
cs.CopyTo(decMs);
toReturn = decMs.ToArray();
}
}
}
}
return toReturn;
}
For encryption, I used very similar code; maybe something's wrong here:
public byte[] GetEncrypted()
{
byte[] toReturn;
using (Aes enc = Aes.Create())
{
enc.Key = Key;
enc.GenerateIV();
IV = enc.IV;
ICryptoTransform cryptoTransform = enc.CreateEncryptor(enc.Key, enc.IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write))
{
cs.Write(Data, 0, Data.Length);
toReturn = ms.ToArray();
}
}
}
return toReturn;
}
Your problem indeed lies in your encrypt side. You must close the crypto stream to flush the data out to the underlying stream first. It is a easy fix, just move your .ToArray() outside of CryptoStream's using block.
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write))
{
cs.Write(Data, 0, Data.Length);
}
toReturn = ms.ToArray();
}

Categories

Resources