Расчет размера зашифрованного файла не соответствует истинному размеру

Мне нужно рассчитать размер файла, который я шифрую с помощью Rijndael.

Согласно другим ответам на этом сайте и в Google, правильный способ расчета длины зашифрованных данных:

EL = UL + (BS - (UL Mod BS) Mod BS)

Where: 
EL = Encrypted Length
UL = Unencrypted Length
BS = Block Size

В моем случае длина незашифрованного файла составляет 5 101 972 байта, и я использую 128-битный ключ шифрования, что дает мне размер блока 16 байтов. Следовательно, уравнение выглядит следующим образом:

EL = 5101972 + (16 - (5101972 Mod 16) Mod 16)
EL = 5101972 + (16 - 4 Mod 16)
EL = 5101972 + (12 Mod 16)
EL = 5101972 + 12
EL = 5101984

Получение зашифрованного файла длиной 5 101 984 байта.

Однако размер моего файла после шифрования составляет 5 242 896 байт. Огромная разница в размерах - 140 912 байт!

Теперь ... Я явно делаю ЧТО-ТО неправильно, но не могу понять, что именно. Ниже приведен мой тестовый код шифрования и дешифрования, а также метод, используемый для расчета зашифрованного размера:

private static void Enc(string decryptedFileName, string encryptedFileName)
{
    PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
    byte[] passwordBytes = passwordDB.GetBytes(128 / 8);

    using (FileStream fsOutput = File.OpenWrite(encryptedFileName))
    {
        using(FileStream fsInput = File.OpenRead(decryptedFileName))
        {
            byte[] IVBytes = Encoding.ASCII.GetBytes("1234567890123456");

            fsOutput.Write(BitConverter.GetBytes(fsInput.Length), 0, 8);
            fsOutput.Write(IVBytes, 0, 16);

            RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.Zeros};
            ICryptoTransform encryptor = symmetricKey.CreateEncryptor(passwordBytes, IVBytes);

            using (CryptoStream cryptoStream = new CryptoStream(fsOutput, encryptor, CryptoStreamMode.Write))
            {
                for (long i = 0; i < fsInput.Length; i += chunkSize)
                {
                    byte[] chunkData = new byte[chunkSize];
                    int bytesRead = 0;
                    while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
                    {
                        cryptoStream.Write(chunkData, 0, chunkSize);
                    }
                }
            }
        }
    }            
}

private static void Dec(string encryptedFileName, string decryptedFileName)
{
    PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
    byte[] passwordBytes = passwordDB.GetBytes(128 / 8);

    using (FileStream fsInput = File.OpenRead(encryptedFileName))
    {
        using (FileStream fsOutput = File.OpenWrite(decryptedFileName))
        {
            byte[] buffer = new byte[8];
            fsInput.Read(buffer, 0, 8);

            long fileLength = BitConverter.ToInt64(buffer, 0);

            byte[] IVBytes = new byte[16];
            fsInput.Read(IVBytes, 0, 16);


            RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.Zeros};
            ICryptoTransform decryptor = symmetricKey.CreateDecryptor(passwordBytes, IVBytes);

            using (CryptoStream cryptoStream = new CryptoStream(fsOutput, decryptor, CryptoStreamMode.Write))
            {
                for (long i = 0; i < fsInput.Length; i += chunkSize)
                {
                    byte[] chunkData = new byte[chunkSize];
                    int bytesRead = 0;
                    while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
                    {
                        cryptoStream.Write(chunkData, 0, bytesRead);
                    }
                }
                fsOutput.SetLength(fileLength);
            }                    
        }
    }
}

private static void CalcEncSize(string decryptedFileName)
{
    FileInfo fi = new FileInfo(decryptedFileName);
    if (fi.Exists)
    {                
        long blockSize = 128/8;
        long fileLength = fi.Length;
        long encryptedSize = fileLength + ((blockSize - (fileLength % blockSize)) % blockSize);
        encryptedSize += 24; //16 bytes for the IV, and 8 more for the filelength, both stored at the start of the file.

        Console.WriteLine("Estimated Encryption Size: " + encryptedSize.ToString());           
    }
}

Примечание: в вычислениях в начале я НЕ включаю дополнительные 24 байта, которые я использую в начале зашифрованного файла для хранения исходной длины файла, и IV ... Я знаю это, но не хотите усложнить уравнение больше, чем необходимо.


person Sk93    schedule 28.07.2010    source источник
comment
Какое значение у chunkSize?   -  person SwDevMan81    schedule 28.07.2010
comment
размер блока в настоящее время составляет 16 байт. Однако, насколько я могу судить, это не влияет на общий зашифрованный размер (с шагом 16, от 16 до 10240).   -  person Sk93    schedule 28.07.2010
comment
Это то, что вы использовали для большого файла? Я полагаю, вы использовали что-то большее   -  person SwDevMan81    schedule 28.07.2010
comment
Вы ведь понимаете, что говорите о 2%, верно? Где вы берете реальный размер? Из файловой системы или из потока шифрования? Количество байтов в памяти и размер на диске могут варьироваться в зависимости от того, как они размещены (хотя обычно не так много, но я никогда не беспокоился о 2%).   -  person Jim L    schedule 28.07.2010
comment
Проблема в том, что я записываю этот файл в определенную часть другого файла. По сути, у меня есть файл размером 4 ГБ, байты содержимого которого состоят из нескольких файлов меньшего размера. Я ДОЛЖЕН рассчитать необходимое пространство (жадные оценки - это нормально, но не наоборот), чтобы убедиться, что я действительно могу поместить этот недавно зашифрованный файл в более крупный файл. Что-то вроде внутренней файловой системы, если хотите.   -  person Sk93    schedule 28.07.2010


Ответы (1)


Вы не должны писать в выходной файл до фактического процесса шифрования. CryptoStream будет обрабатывать все необходимые отступы, когда он закрыт. Также цикл for не нужен, поскольку внутренний цикл while будет читать весь файл. Также вы должны писать ровно столько, сколько было прочитано из файла. Попробуйте эти изменения.

private static void Enc(string decryptedFileName, string encryptedFileName)
{
    PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
    byte[] passwordBytes = passwordDB.GetBytes(128 / 8);

    using (FileStream fsOutput = File.OpenWrite(encryptedFileName))
    {
        using(FileStream fsInput = File.OpenRead(decryptedFileName))
        {
            byte[] IVBytes = Encoding.ASCII.GetBytes("1234567890123456");

            RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.Zeros};
            ICryptoTransform encryptor = symmetricKey.CreateEncryptor(passwordBytes, IVBytes);

            using (CryptoStream cryptoStream = new CryptoStream(fsOutput, encryptor, CryptoStreamMode.Write))
            {
                byte[] chunkData = new byte[chunkSize];
                int bytesRead = 0;
                while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
                {
                    cryptoStream.Write(chunkData, 0, bytesRead);  //KEY FIX
                }
            }
        }
    }            
}

[редактировать]

О, я пропустил много информации. Я неправильно понял, что ваши размеры думали, что 140 912 было размером, а не разницей. Теперь, когда я это вижу, я могу дать более внятный ответ. Исходя из вашего кода, разница в размерах должна быть сопоставима с размером вашего блока. Поскольку chunkSize может быть несколько большим, ваш код обычно записывает на chunkSize больше данных, чем на самом деле во входном файле (как указали Грег и caf). Выделенная мною линия является причиной несоответствия.

person Jeff Mercado    schedule 28.07.2010
comment
Вы можете объяснить, почему я не могу писать в поток до шифрования? Данные, которые я пишу в самом начале (первые 24 байта), не хотят шифровать, так как мне нужно расшифровать остальные. На самом деле, похоже, нет никакой разницы, использую ли я chunkSize или bytesRead, поскольку любые неиспользуемые байты в массиве chuckData просто нулевые и, похоже, в любом случае не кодируются методом cryptoStream.Write (). Если бы это было не так, не повлияло бы это отрицательно на процесс шифрования / дешифрования, который работает отлично? - person Sk93; 28.07.2010
comment
Что касается записи в файл перед шифрованием, полагаю, это было бы хорошо. Просто вам нужно помнить, что если вы попытаетесь расшифровать, вам нужно смещать эти записанные байты, чтобы правильно расшифровать данные. Это действительно не должно влиять на процесс шифрования, но влияет на расшифровку. Я обновлю свой ответ, чтобы напрямую обратиться к тому, что, по-видимому, вызывает проблему. - person Jeff Mercado; 28.07.2010
comment
Извините, мне придется подумать об этом. Я думал, что у меня будет разумное объяснение, но в данный момент нет (а у меня мало времени). Я помог другому человеку решить почти ту же проблему, но так и не получил ответа о конечном результате. Я вернусь к вам по этому поводу. - person Jeff Mercado; 28.07.2010
comment
Вы были правы, я думаю, что эти изменения не должны повлиять на процесс. Единственное другое возможное объяснение, которое я мог придумать, это то, что CryptoStream закрывается до того, как он сможет закончить запись в выходной файл. Одинакова ли длина зашифрованного файла каждый раз, когда вы пытаетесь зашифровать один и тот же файл? Мои личные тесты показывают, что он дает разумные размеры файлов для файлов, которые я тестировал. - person Jeff Mercado; 29.07.2010
comment
Ваш код правильно использует bytesRead, а OP - нет. Это серьезная ошибка в коде OP. Все байты размера фрагмента шифруются каждый раз во время цикла, независимо от того, сколько байтов было фактически прочитано. - person President James K. Polk; 29.07.2010
comment
GregS - см. Мой первоначальный комментарий. Я вообще не верю, что это так серьезно ... Это уж точно не имеет отношения к этому вопросу. - person Sk93; 30.07.2010
comment
@ Sk93: @GregS и Jeff M верны - в вашем коде эти лишние 0 байтов будут благополучно зашифрованы и записаны, как если бы они были частью исходных незашифрованных данных - CryptoStream не знает разницы. Вот почему ваш файл так сильно расширяется. - person caf; 30.07.2010
comment
Ах, я совершенно неправильно понял проблему в вопросе. Обновил мой ответ, который это объясняет. (и было бы неплохо узнать причину отрицательного голоса) - person Jeff Mercado; 30.07.2010