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