Недопустимая длина данных для дешифрования.

Я пытаюсь зашифровать и расшифровать поток файлов через сокет, используя RijndaelManaged, но я продолжаю натыкаться на исключение

CryptographicException: Length of the data to decrypt is invalid.
    at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
    at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
    at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing)

Исключение выдается в конце инструкции using в receiveFile, когда весь файл был передан.

Я попробовал поиск в Интернете, но нашел ответы на проблемы, возникающие при использовании Encoding при шифровании и расшифровке одной строки. Я использую FileStream, поэтому я не указываю кодировку, которая будет использоваться, поэтому это не должно быть проблемой. Это мои методы:

private void transferFile(FileInfo file, long position, long readBytes)
{
    // transfer on socket stream
    Stream stream = new FileStream(file.FullName, FileMode.Open);
    if (position > 0)
    {
        stream.Seek(position, SeekOrigin.Begin);
    }
    // if this should be encrypted, wrap the encryptor stream
    if (UseCipher)
    {
        stream = new CryptoStream(stream, streamEncryptor, CryptoStreamMode.Read);
    }
    using (stream)
    {
        int read;
        byte[] array = new byte[8096];
        while ((read = stream.Read(array, 0, array.Length)) > 0)
        {
            streamSocket.Send(array, 0, read, SocketFlags.None);
            position += read;
        }
    }
}

private void receiveFile(FileInfo transferFile)
{
    byte[] array = new byte[8096];
    // receive file
    Stream stream = new FileStream(transferFile.FullName, FileMode.Append);
    if (UseCipher)
    {
        stream = new CryptoStream(stream, streamDecryptor, CryptoStreamMode.Write);
    }
    using (stream)
    {
        long position = new FileInfo(transferFile.Path).Length;
        while (position < transferFile.Length)
        {
            int maxRead = Math.Min(array.Length, (int)(transferFile.Length - position));
            int read = position < array.Length
                        ? streamSocket.Receive(array, maxRead, SocketFlags.None)
                        : streamSocket.Receive(array, SocketFlags.None);
            stream.Write(array, 0, read);
            position += read;
        }
    }
}

Это метод, который я использую для настройки шифров. byte [] init - это сгенерированный массив байтов.

private void setupStreamCipher(byte[] init)
{
    RijndaelManaged cipher = new RijndaelManaged();
    cipher.KeySize = cipher.BlockSize = 256; // bit size
    cipher.Mode = CipherMode.ECB;
    cipher.Padding = PaddingMode.ISO10126;
    byte[] keyBytes = new byte[32];
    byte[] ivBytes = new byte[32];

    Array.Copy(init, keyBytes, 32);
    Array.Copy(init, 32, ivBytes, 0, 32);

    streamEncryptor = cipher.CreateEncryptor(keyBytes, ivBytes);
    streamDecryptor = cipher.CreateDecryptor(keyBytes, ivBytes);
}

У кого-нибудь есть идея в том, что я могу делать неправильно?

Ответ 1

Мне кажется, что вы не отправляете последний блок. Вам нужно как минимум FlushFinalBlock() отправить CryptoStream, чтобы гарантировать, что отправляется последний блок (который ищет поисковый поток).

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

РЕДАКТИРОВАТЬ: Упс, поток шифрования находится в режиме чтения. В этом случае вам нужно убедиться, что вы читаете EOF, чтобы CryptoStream мог работать с последним блоком, а не останавливаться после readBytes. Вероятно, легче управлять, если вы запускаете поток шифрования в режиме записи.

Еще одно замечание: вы не можете предположить, что байты в байтах равны. Блокированные шифры имеют фиксированный размер блока, который они обрабатывают, и если вы не используете режим шифрования, который преобразует блочный шифр в потоковый шифр, будет добавление, которое зашифрует текст дольше, чем открытый текст.

Ответ 2

После комментария, сделанного Джеффри Хантином, я изменил некоторые строки в getFile на

using (stream) {
    FileInfo finfo = new FileInfo(transferFile.Path);
    long position = finfo.Length;
    while (position < transferFile.Length) {
        int maxRead = Math.Min(array.Length, (int)(transferFile.Length - position));
        int read = position < array.Length
                   ? streamSocket.Receive(array, maxRead, SocketFlags.None)
                   : streamSocket.Receive(array, SocketFlags.None);
        stream.Write(array, 0, read);
        position += read;
    }
}

->

using (stream) {
    int read = array.Length;
    while ((read = streamSocket.Receive(array, read, SocketFlags.None)) > 0) {
        stream.Write(array, 0, read);
        if ((read = streamSocket.Available) == 0) {
            break;
        }
    }
}

И вуаля, она работает (из-за того, что я так добрался, что мне не хотелось беспокоиться раньше). Я не уверен, что произойдет, если Available вернет 0, хотя все данные не были переданы, но я буду склонен к этому позже в этом случае. Спасибо за помощь Джеффри!

С уважением.

Ответ 3

cipher.Mode = CipherMode.ECB;

Argh! Прокрутка собственного кода безопасности почти всегда плохая идея.

Ответ 4

Моя просто удалила прокладку, и она работает

Прокомментировал это - cipher.Padding = PaddingMode.ISO10126;