2014-10-17 43 views
1

我使用Bouncy Castle庫來加密我的Windows應用商店中的一些數據。我EncryptHelper類:Bouncy Castle AES Encryption - 提供塊輸入

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(); 
     } 
    } 
} 

Process()方法工作正常,但是當指令

inCount = await sourceStream.ReadAsync(inBuffer, 0, inBuffer.Length); 

我怕如果流有太多的數據可能occurr一個OutOfMemoryException。所以,我試圖構建ProcessBlocks()方法,它應該逐步從流中讀取,每次一個塊,而不會對RAM過度充電。我對如何處理outBuffer有一些疑問:它應該在執行cipher.ProcessBytes()的每個循環中被覆蓋,但是在調用the cipher.DoFinal()之前它應該在哪個大小上執行?

謝謝

UPDATE 30/07/2015

我修改了主要的回答來處理一個zip文件和outcoming zip文件不再是一個有效的ZIP,可能有人給我解釋一下爲什麼?

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(); 
     } 
+0

你應該非常小心地混合加密與異步,或任何不按順序。你已被警告:) – 2014-10-18 14:29:32

+0

@owlstead:所有的異步方法都在等待,所以應該沒有問題 – Federinik 2014-10-20 14:09:32

+0

這取決於你如何處理調用代碼中用作參數的密碼和流,不是嗎?我並不是一位擅長「async/await」的專家,我只知道密碼和數據流並不是用來處理併發和/或交錯式呼叫的。 – 2014-10-20 17:30:15

回答

2

cipher.DoFinal()將生產多達2 * Cipher.GetBlockSize()字節。實際產生的字節數由方法返回。

下面是一個基於您的示例鬆散的例子。

using System; 
using System.IO; 

using Org.BouncyCastle.Crypto.Paddings; 
using Org.BouncyCastle.Crypto.Parameters; 
using Org.BouncyCastle.Crypto.Modes; 
using Org.BouncyCastle.Crypto.Engines; 
using System.Text; 

namespace PaddedBufferedBlockCipherExample 
{ 
    public class EncryptHelper 
    { 
     private const string KEY = "chiaveAES"; 
     private const int BufferSize = 1024; 
     private PaddedBufferedBlockCipher cipher; 

     public enum CipherMode 
     { 
      Encrypt, 
      Decrypt 
     } 

     public EncryptHelper (CipherMode mode) 
     { 
      cipher = new PaddedBufferedBlockCipher (new CbcBlockCipher (new AesLightEngine()), new Pkcs7Padding()); 

      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)); 
     } 

     public static void Encrypt (Stream sourceStream, Stream destinationStream) 
     { 
      var helper = new EncryptHelper (CipherMode.Encrypt); 
      helper.ProcessBlocks (sourceStream, destinationStream); 
     } 


     public static void Decrypt (Stream sourceStream, Stream destinationStream) 
     { 
      var helper = new EncryptHelper (CipherMode.Decrypt); 
      helper.ProcessBlocks (sourceStream, destinationStream); 
     } 


     private void ProcessBlocks (Stream sourceStream, Stream destinationStream) 
     { 


      // inBuffer is sized for efficient I/O 
      var inBuffer = new byte[BufferSize]; 

      // outBuffer should be large enough to not require further resizing 
      var outBuffer = new byte[cipher.GetBlockSize() + cipher.GetOutputSize (inBuffer.Length)]; 
      int inCount = 0; 
      int outCount = 0; 

      // Process data using the cipher.ProcessBytes method, until we reach EOF 

      while ((inCount = sourceStream.Read (inBuffer, 0, inBuffer.Length)) > 0) { 
       outCount = cipher.ProcessBytes (inBuffer, 0, inCount, outBuffer, 0); 
       destinationStream.Write (outBuffer, 0, outCount); 
      } 

      // Now "flush" the cipher instance by calling the DoFinal method. This 
      // will finish the en/de-cryption by ciphering any buffered data and processing any 
      // encryption padding. 

      outCount = cipher.DoFinal (outBuffer, 0); 

      destinationStream.Write (outBuffer, 0, outCount); 
     } 

     public static void Main (string[] args) 
     { 
      var plainPath = "/Users/robert/src/csharp_toys/toy1/Program.cs"; 
      var plainStream = new FileStream (plainPath, FileMode.Open, FileAccess.Read); 
      var cipherStream = new MemoryStream(); 
      EncryptHelper.Encrypt (plainStream, cipherStream); 
      cipherStream.Seek (0, SeekOrigin.Begin); 
      var decryptedStream = new MemoryStream(); 
      EncryptHelper.Decrypt (cipherStream, decryptedStream); 
      var decryptedString = Encoding.ASCII.GetString (decryptedStream.ToArray()); 
      Console.Write (decryptedString); 
     } 
    } 
} 
+0

outBuffer = new byte [2 * cipher.GetBlockSize()]; outCount + = cipher.DoFinal(outBuffer,outBuffer.Length);拋出DataLengthException:輸出緩衝區太短 – Federinik 2014-10-18 07:58:48

+1

@Federinik:DoFinal的最後一個參數是* not *長度,它是您希望DoFinal開始寫入密文的緩衝區的偏移量。對於你的例子,它應該是0. – 2014-10-18 16:22:02

+1

@GregS我試過你的解決方案,它的工作原理。謝謝。希望BouncyCastle能夠提供更好的文檔和參數說明。 – Federinik 2014-10-20 14:26:57

0

UPDATE 30/07/2015

我發現這只是一個填充問題,我用ZeroBytePadding,它搞砸了一切。

相關問題