2017-01-19 2 views
4

Я новичок в шифрования и я шифрование файлов, используя следующий метод:Многопоточность шифрования в C#

private static void encryptFile(string filePath, byte[] password, byte[] salt) 
{ 
    Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes(password, salt, 1000); 
    AesManaged algorithm = new AesManaged(); 

    byte[] rgbKey = rdb.GetBytes(algorithm.KeySize/8); 
    byte[] rgbIV = rdb.GetBytes(algorithm.BlockSize/8); 
    GCHandle keyHandle = GCHandle.Alloc(rgbKey, GCHandleType.Pinned); 
    GCHandle IVHandle = GCHandle.Alloc(rgbIV, GCHandleType.Pinned); 

    ICryptoTransform cryptoAlgorithm = algorithm.CreateEncryptor(rgbKey, rgbIV); 

    using (FileStream readStream = File.Open(filePath, FileMode.Open)) 
    { 
     using (FileStream writeStream = new FileStream(filePath + ".enc", FileMode.Create, FileAccess.Write)) 
     { 
      using (CryptoStream cryptoStream = new CryptoStream(writeStream, cryptoAlgorithm, CryptoStreamMode.Write)) 
      { 
       while (readStream.Position < readStream.Length) 
       { 
        byte[] buffer = new byte[4096]; 
        int amountRead = readStream.Read(buffer, 0, buffer.Length); 
        cryptoStream.Write(buffer, 0, amountRead); 
       } 
       cryptoStream.Flush(); 
      } 
     } 
    } 

    UtilityMethods.destroyBytes(rgbKey); 
    UtilityMethods.destroyBytes(rgbIV); 
    keyHandle.Free(); 
    IVHandle.Free(); 
} 

То, что я хочу сделать, это распараллелить процесс для более быстрого шифрования. Используя один поток, это заняло более 5 минут, чтобы зашифровать файл размером ~ 3 ГБ. Я ищу, чтобы иметь возможность сделать это шифрование менее чем за 1 мин, если это возможно (менее 30 секунд будет фантастическим, но я думаю, что могу растягиваться).

Я считаю, что ответ заключается в создании нескольких потоков (хотя я не уверен), назначая каждому потоку фрагмент файла для шифрования, но я не уверен, как «разбить файл», чтобы назначить кусок для каждого потока или «поместить файл обратно вместе» после того, как каждая часть прошла через поток, которому он был назначен. Может ли кто-нибудь указать мне в правильном направлении?

Большое спасибо!

P.S. Я просмотрел это (Rijndael algorithm and CryptoStream: is it possible to encrypt/decrypt multithreaded?), но я не понимаю ответа (ECB, CBC?). Если ответ на мой вопрос лежит там, вы могли бы предоставить какой-то образец кода, чтобы заставить меня двигаться в правильном направлении?

Еще раз спасибо!

+0

Параллельное/многопоточное шифрование возможно с ECB только потому, что в противном случае один блок зависит друг от друга.О ECB, CBC: см. [Режимы работы шифрования: ECB vs CBC] (https://adayinthelifeof.nl/2010/12/08/encryption-operating-modes-ecb-vs-cbc/) и [Перечисление CipherMode] (https : //msdn.microsoft.com/en-US/library/system.security.cryptography.ciphermode (v = vs.110) .aspx). Обратите внимание также на замечание об ECB: «Важно: этот режим не рекомендуется, поскольку он открывает дверь для нескольких подделок безопасности». –

+0

Это проблема ввода-вывода, а не проблема с ЦП. Попробуйте использовать тот же код со входом и выходом на отдельных физических дисках или с SSD, и вы увидите лучшую производительность. –

ответ

0

Для потока чтения вам необходимо использовать Stream.Seek. Посмотрите на вопрос: StreamReader and seeking , чтобы помочь вам избежать распространенных ошибок.

Имейте в виду, что вам нужно будет запомнить начало & положение остановки каждого потока точно (в частности, зашифрованную длину потока), иначе вы не сможете расшифровать свои фрагменты файлов.

Удачи вам!

2

CBC (Cipher Block Chaining) - это режим блокировки по умолчанию для AesManaged.

Вы не можете зашифровывать различные блоки параллельно AES, если вы используете CBC, потому что побочным эффектом блока шифрования 1, например, является установка нового IV для блока 2. Это называется «обратная связь IV». Если вы работаете параллельно, это не сработает. Вам нужно будет выбрать другой cipher block mode.

Вы можете использовать режим ECB (который не имеет обратной связи с IV), но если вы это сделаете, блоки, которые повторяются в виде открытого текста, также будут повторяться в зашифрованном тексте, что откроет вам определенные типы атак.

Лучше всего использовать CTR, где IV основан на счетчике, но, увы, .NET, похоже, не поддерживает его.

Я предлагаю вам разбить один файл на несколько «виртуальных» файлов, каждый со своим собственным шифровым потоком. Используйте CBC и заполняйте IV байтами из RNGCryptoServiceProvider. Выделите RNGCryptoServiceProvider со значением, полученным из индекса виртуального файла в физическом файле. Таким образом, у вас есть IV, который меняется от блока к блоку, а повторный открытый текст не будет отображаться в зашифрованном тексте. Повторите процесс при расшифровке.

См. Также this question.

Предлагаю вам отправить сообщение на номер security.stackexchange.com для просмотра, когда вы будете готовы.

+0

Я согласен с вашим ответом, но я не уверен, что согласен с вашим решением. Разве вы не думаете, что реализация режима CTR сама (что относительно прямолинейно) будет лучшим подходом, чем то, что вы предлагаете? Интересует ваше мнение. –

+0

Если производительность является проблемой, которую мы решаем, внедрение математического шифрования в C# может оказаться нецелесообразным. Мое предлагаемое решение * должно быть ОК, или, по крайней мере, не хуже, чем использование CBC снова и снова. Но вы правы, это опасно, когда вы сворачиваете свою собственную безопасность. Вероятно, лучшим ответом является покупка сторонней реализации CTR и ее использование. –

+0

Как интенсивна CTR? Используется больше операций xor, чем режим CBC. Или вы говорите о скорости скомпилированного C#? –

Смежные вопросы