1

我正在使用 Bouncy Castle 库来加密我的 Windows 应用商店应用程序中的一些数据。我的EncryptHelper班级:

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

如果流有太多数据,我担心它可能会发生 OutOfMemoryException。因此,我试图构建该ProcessBlocks()方法,该方法应逐步从流中读取,每次一个块,而不会对 RAM 过度充电。我对如何处理有一些疑问outBuffer:它应该在执行 cipher.ProcessBytes() 的每个周期中被覆盖,但是在the cipher.DoFinal()调用之前它应该是什么大小?

谢谢

2015 年 7 月 30 日更新

我修改了答案中的 Main 来处理一个 zip 文件,而输出的 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();
        }
4

2 回答 2

4

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);
        }
    }
}
于 2014-10-17T22:01:38.827 回答
0

2015 年 7 月 30 日更新

我发现这只是一个填充问题,我使用了ZeroBytePadding,它把一切都搞砸了。

于 2015-07-30T10:04:00.360 回答