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

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

Я попытался найти в интернете, но нашел только ответы на проблемы, возникающие при использовании кодирования при шифровании и расшифровке одной строки. Я использую FileStream, поэтому я не указываю кодировку для использования, так что проблема не в этом. Вот мои методы:--4-->

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);
}

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

4 ответов


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

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

EDIT: Упс, поток шифрования находится в режиме чтения. В этом случае вам нужно убедиться, что Вы читаете EOF, чтобы CryptoStream мог иметь дело с окончательным блоком, а не останавливаться после readBytes. Вероятно, легче контролировать, если вы запускаете поток шифрования в режиме записи.

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


после замечания, сделанного Джеффри Hantin, я изменил несколько строк в receiveFile к

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;
        }
    }
}

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

С уважением.


cipher.Mode = CipherMode.ECB;

Аргх! Сворачивание собственного кода безопасности-почти всегда плохая идея.


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

прокомментировал этот шифр.Padding = PaddingMode.ISO10126;