Почему я получаю System.Security.Cryptography.CryptographicException во время расшифровки этого массива байтов

Я только начал с криптографии на С# и попытался сначала зашифровать, а затем расшифровать файл. Но во время расшифровки в функции private static byte[] Decrypt(byte[] inputBuffer) , в byte[] outputBuffer = transform.TransformFinalBlock (inputBuffer, 0, inputBuffer.Length); Я получаю следующее исключение: System.Security.Cryptography.CryptographicException: «Недопустимые данные». Но почему?

Это функция для чтения файла:

private static void DecryptFile()
{
    byte[] buffer = new byte[4096];
    byte[] decryptionBuffer = new byte[4096];
    int bytesRead = 0;
    using (FileStream inputStream = File.OpenRead(Environment.CurrentDirectory + "\\Encrypted.txt"))
    {
        using (FileStream outputStream = File.Create(Environment.CurrentDirectory + "\\Decrypt.mp3"))
        {
            while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                decryptionBuffer = Decrypt(buffer);
                outputStream .Write(decryptionBuffer, 0, decryptionBuffer.Length);
            }
        }
        Console.WriteLine("Done.");
    }
}

Это функция для расшифровки файла, ключа и вектора инициализации:

private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };

private static byte[] Decrypt(byte[] inputBuffer)
{
    SymmetricAlgorithm algorithm = DES.Create();
    ICryptoTransform transform = algorithm.CreateDecryptor(key, iv);
    //here the exception is triggered
    byte[] outputBuffer = transform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
    return outputBuffer;
}

вот как файл был зашифрован:

private static void EncryptFile()
{
    byte[] buffer = new byte[4096];
    byte[] enryptionBuffer = new byte[4096];
    int bytesRead = 0;
    using (FileStream inputStream = File.OpenRead(Environment.CurrentDirectory + "\\Test.txt"))
    {
        using (FileStream outputStream = File.Create(Environment.CurrentDirectory + "\\Encrypted.txt"))
        {
            while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                encryptionBuffer = Encrypt(buffer);
                outputStream .Write(encryptionBuffer, 0, encryptionBuffer.Length);
            }
        }
        Console.WriteLine("Done.");
    }
}

//Key and initialization vector are the same
private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };

private static byte[] Encrypt(byte[] inputBuffer)
{
    SymmetricAlgorithm algorithm = DES.Create();
    ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
    byte[] outputBuffer = transform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
    return outputBuffer;
}

person Kravag    schedule 08.01.2019    source источник
comment
Как был зашифрован файл? Вы уверены, что при шифровании использовались одни и те же IV и ключ?   -  person S.L. Barth    schedule 08.01.2019
comment
Вы расшифровываете каждый блок заново. Вряд ли это было так, как это было зашифровано.   -  person bommelding    schedule 08.01.2019
comment
Свойства, которые использовались для шифрования, отличаются от тех, которые вы используете для расшифровки. DES имеет блоки и параметры разного размера, и вы получите исключение, если параметры не совпадают с параметрами, используемыми для шифрования.   -  person jdweng    schedule 08.01.2019
comment
@SLBarth да, я только что добавил, как я его зашифровал.   -  person Kravag    schedule 08.01.2019
comment
Лично я больше не называл бы однократное шифрование DES. В настоящее время это в лучшем случае запутывание.   -  person Maarten Bodewes    schedule 08.01.2019


Ответы (2)


Ваш код шифрования создает буферы вывода большего размера, чем буферы ввода. Если вы независимо шифруете 4096 байт за раз, на выходе получается 4104 байта1. Если вы хотите продолжать независимо шифровать каждый блок вашего файла, вам нужно изменить свой код дешифрования, чтобы использовать буферы по 4104 байта, когда он работает в кусках.

Однако в идеале это «шифрование каждого блока отдельно» не является обязательным. В этом случае создайте объект transform один раз, а не один раз каждый раз в цикле. И используйте Transform вместо TransformFinalBlock до тех пор, пока не узнаете, что достигли конца файла (обратите внимание, однако, что они возвращают очень разные вещи).

Вы также игнорируете bytesRead, который говорит вам, сколько вашего буфера заполнено полезными данными. Вам также нужно использовать это, и не делайте ваш окончательный раунд шифрования равным x многим байтам, которые являются последними байтами файла, и bufferSize - x байтами предыдущего блока данных из файла.

Я бы, вероятно, вместо этого создал CryptoStream, который обертывал один из ваших FileStream объектов, а затем использовал Stream.CopyTo или моральные эквиваленты для выполнения этой работы. Пусть библиотеки заботятся об управлении буферами, циклами и т. д.

И, наконец, в идеале вы понимаете, что сейчас 2019 год, и поэтому совершенно неуместно писать новый код, использующий DES для шифрования2.


1Эта программа, если вы установите точку останова на строке Console.ReadLine, имеет c, содержащую 4104 байта:

using System;
using System.Security.Cryptography;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var b = new byte[4096];
        var c = Encrypt(b);
        Console.ReadLine();
    }

    private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
    private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };

    private static byte[] Encrypt(byte[] inputBuffer)
    {
        SymmetricAlgorithm algorithm = DES.Create();
        ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
        byte[] outputBuffer = transform.TransformFinalBlock(
                                inputBuffer,
                                0,
                                inputBuffer.Length);
        return outputBuffer;
    }
}

2Итак, мой EnryptFile в целом будет таким:

private static void EncryptFile()
{
    using (var inputStream = File.OpenRead(Environment.CurrentDirectory + "\\Test.txt"))
    using (var outputStream = File.Create(Environment.CurrentDirectory + "\\Encrypted.txt"))
    using (var aes = Aes.Create())
    using (var cStream = new CryptoStream(
                               inputStream,
                               aes.CreateEncryptor(key, iv),
                               CryptoStreamMode.Read))
    {
        cStream.CopyTo(outputStream);
    }
}

Или вариант async, который использует await cStream.CopyToAsync(outputStream); в качестве самого внутреннего оператора. DecryptFile будет аналогичным образом упрощен.

person Damien_The_Unbeliever    schedule 08.01.2019

Я столкнулся с той же проблемой, после чего создал настраиваемую функцию для шифрования/дешифрования, и с помощью этой функции также поддерживает большие файлы, потому что мы читаем и записываем файл по частям. есть EncryptMode, который вам нужен с помощью этого метода, например, если вы хотите зашифровать, отправьте _mode как _mode.ENCRYPT, а если вы хотите расшифровать, отправьте _mode как _mode.DECRYPT.

частное перечисление EncryptMode {ШИФРОВАТЬ, РАСШИФРОВАТЬ};

public void encryptDecryptChunkByChunk (строка _inputPath, строка _outputPath, строка _encryptionKey, EncryptMode _mode, строка _initVector) {

        string _out = "";// output string
        //_encryptionKey = MD5Hash (_encryptionKey);
        _pwd = Encoding.UTF8.GetBytes(_encryptionKey);
        _ivBytes = Encoding.UTF8.GetBytes(_initVector);

        int len = _pwd.Length;
        if (len > _key.Length)
        {
            len = _key.Length;
        }
        int ivLenth = _ivBytes.Length;
        if (ivLenth > _iv.Length)
        {
            ivLenth = _iv.Length;
        }

        Array.Copy(_pwd, _key, len);
        Array.Copy(_ivBytes, _iv, ivLenth);
        _rcipher.Key = _key;
        _rcipher.IV = _iv;

        if (_mode.Equals(EncryptMode.ENCRYPT))
        {
            //encrypt
            using (FileStream fs = new FileStream(_inputPath, FileMode.Open, FileAccess.Read))
            {
                using (BinaryReader br = new BinaryReader(fs, new ASCIIEncoding()))
                {
                    System.IO.StreamWriter file = new System.IO.StreamWriter(_outputPath);
                    try
                    {
                        byte[] chunk;

                        chunk = br.ReadBytes(CHUNK_SIZE);
                        while (chunk.Length > 0)
                        {
                            var base641 = Convert.ToBase64String(chunk);
                            //DumpBytes(chunk, chunk.Length);
                            chunk = br.ReadBytes(CHUNK_SIZE);
                            var base64 = Convert.ToBase64String(chunk);

                            byte[] plainText = _rcipher.CreateEncryptor().TransformFinalBlock(_enc.GetBytes(base641), 0, base641.Length);
                            var bas64Encrypted = Convert.ToBase64String(plainText);
                            //fsCrypt.Write(bas64Encrypted);
                            file.WriteLine(bas64Encrypted);
                        }
                        file.Close();
                    }
                    catch (Exception ex)
                    {
                        file.Close();
                    }
                }
            }
        }
        if (_mode.Equals(EncryptMode.DECRYPT))
        {
            FileStream fsOut = new FileStream(_outputPath, FileMode.OpenOrCreate, FileAccess.Write);
            try
            {
                foreach (string line in File.ReadLines(_inputPath))
                {
                    // Process your line here....
                    var p = line;
                    var x2 = Convert.FromBase64String(p);
                    byte[] plainText = _rcipher.CreateDecryptor().TransformFinalBlock(x2, 0, x2.Length);
                    var y1 = _enc.GetString(plainText);
                    var y2 = Convert.FromBase64String(y1);
                    fsOut.Write(y2, 0, y2.Length);
                }
                fsOut.Close();
            }
            catch (Exception ex)
            {
                fsOut.Close();
            }
        }
        _rcipher.Dispose();
    }
person Mayank Kumar    schedule 15.01.2021