Расчет размера зашифрованного файла не соответствует истинному размеру - PullRequest
4 голосов
/ 28 июля 2010

У меня есть необходимость рассчитать размер файла, который я шифрую, используя 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 ... Я знаю это, но не хотел усложнять уравнение более, чем необходимо.

1 Ответ

4 голосов
/ 28 июля 2010

Вы не должны записывать в выходной файл до фактического процесса шифрования. 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 больше данных, чем на самом деле во входном файле (как указывали Грег и Кафе). Отмеченная мною строка является причиной расхождения.

...