I'm using Bouncy Castle library to encrypt some data in my Windows Store App. My EncryptHelper
class:
public static class EncryptHelper
{
private const string KEY = "chiaveAES";
private const int SIZE = 16;
private enum CipherMode
{
Encrypt,
Decrypt
}
private static PaddedBufferedBlockCipher InitCipher(CipherMode mode)
{
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new AesLightEngine()), new ZeroBytePadding());
var key = new byte[32];
var keyArray = KEY.ToCharArray();
Buffer.BlockCopy(keyArray, 0, key, 0, Math.Min(keyArray.Length, key.Length));
cipher.Init(mode == CipherMode.Encrypt, new KeyParameter(key));
return cipher;
}
public static async Task Encrypt(Stream sourceStream, Stream destinationStream, bool autoSeekStart = true, bool autoSeekEnd = true)
{
//await Process(InitCipher(CipherMode.Encrypt), sourceStream, destinationStream, autoSeekStart, autoSeekEnd);
await ProcessBlocks(InitCipher(CipherMode.Encrypt), sourceStream, destinationStream, autoSeekStart, autoSeekEnd);
}
public static async Task Decrypt(Stream sourceStream, Stream destinationStream, bool autoSeekStart = true, bool autoSeekEnd = true)
{
//await Process(InitCipher(CipherMode.Decrypt), sourceStream, destinationStream, autoSeekStart, autoSeekEnd);
await ProcessBlocks(InitCipher(CipherMode.Decrypt), sourceStream, destinationStream, autoSeekStart, autoSeekEnd);
}
private static async Task Process(PaddedBufferedBlockCipher cipher, Stream sourceStream, Stream destinationStream, bool autoSeekStart, bool autoSeekEnd)
{
if (autoSeekStart)
{
sourceStream.ToBegin();
destinationStream.ToBegin();
}
var size = Convert.ToInt16(sourceStream.Length);
byte[] inBuffer = new byte[size];
byte[] outBuffer = new byte[cipher.GetOutputSize(size)];
int inCount = 0;
int outCount = 0;
try
{
inCount = await sourceStream.ReadAsync(inBuffer, 0, inBuffer.Length);
outCount = cipher.ProcessBytes(inBuffer, 0, inCount, outBuffer, 0);
outCount += cipher.DoFinal(outBuffer, outCount);
await destinationStream.WriteAsync();
await destinationStream.FlushAsync();
}
catch { }
if (autoSeekEnd)
{
sourceStream.ToBegin();
destinationStream.ToBegin();
}
}
private static async Task ProcessBlocks(PaddedBufferedBlockCipher cipher, Stream sourceStream, Stream destinationStream, bool autoSeekStart, bool autoSeekEnd)
{
if (autoSeekStart)
{
sourceStream.ToBegin();
destinationStream.ToBegin();
}
byte[] inBuffer = new byte[SIZE];
byte[] outBuffer = new byte[cipher.GetOutputSize(SIZE)];
int inCount = 0;
int outCount = 0;
try
{
while ((inCount = await sourceStream.ReadAsync(inBuffer, 0, inBuffer.Length)) > 0)
{
outCount += cipher.ProcessBytes(inBuffer, 0, inCount, outBuffer, 0);
await destinationStream.WriteAsync(outBuffer, 0, outBuffer.Length);
}
outBuffer = ?
outCount += cipher.DoFinal(outBuffer, outCount);
await destinationStream.WriteAsync(outBuffer, 0, outCount);
await destinationStream.FlushAsync();
}
catch { }
if (autoSeekEnd)
{
sourceStream.ToBegin();
destinationStream.ToBegin();
}
}
}
My Process()
method works fine, but when on the instruction
inCount = await sourceStream.ReadAsync(inBuffer, 0, inBuffer.Length);
I'm afraid it may occurr an OutOfMemoryException if the stream has too much data. So, I was trying to build the ProcessBlocks()
method, which should read from the stream progressively, one block per time, without overcharging the RAM. I have some doubts on how to behave with outBuffer
: it should be overwritten in every cycle in which cipher.ProcessBytes() gets executed, but of which size should it be just before the cipher.DoFinal()
invocation?
Thank you
UPDATE 30/07/2015
I modified the Main in the answer to handle a zip file and the outcoming zip file is no more a valid ZIP, could someone explain me why?
public static void Main(string[] args)
{
var plainPath = @"C:\Users\Federico\Desktop\0abed72d-defc-4c9a-a8ae-3fec43f01224.zip";
var decryptPath = @"C:\Users\Federico\Desktop\0abed72d-defc-4c9a-a8ae-3fec43f01224 - decrypted.zip";
var plainStream = new FileStream(plainPath, FileMode.Open, FileAccess.Read);
var cipherStream = new MemoryStream();
EncryptHelper.Encrypt(plainStream, cipherStream);
cipherStream.Seek(0, SeekOrigin.Begin);
FileStream fs = new FileStream(decryptPath, FileMode.Create);
EncryptHelper.Decrypt(cipherStream, fs);
fs.Flush();
fs.Close();
}