Длина данных для расшифровки неверна - PullRequest
12 голосов
/ 03 июня 2009

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

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 ]

6 голосов
/ 03 июня 2009

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

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

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

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

1 голос
/ 04 июня 2009

После комментария, сделанного Джеффри Хантином, я изменил некоторые строки в 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;
        }
    }
}

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

Привет.

0 голосов
/ 08 июля 2016

Мой, я только что удалил отступы, и это работает

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

0 голосов
/ 03 июня 2009
cipher.Mode = CipherMode.ECB;

Argh! Использование собственного кода безопасности - это почти всегда плохая идея.

...