Я разработал систему, в которой хранятся блоки данных, отправленные сжатыми и зашифрованными несколькими клиентами.DCPCrypt (Delphi) + ZLib/«ошибка данных» при распаковке дешифрованных данных
При попытке получить хранимые блоки данных (которые мне нужно выполнить распаковку и дешифрование) на конкретном компьютере возникает исключение «ошибка данных» ZLib во время декомпрессии данных. Кажется очевидным, что поток не был должным образом расшифрован, поэтому входной поток не является допустимым потоком ZLib, что приводит к такой проблеме.
После некоторого исследования я обнаружил, что для идентичного блока исходных данных, которые также хранятся, этот проблемный блок содержит полные разные данные, поэтому совершенно ясно, что в алгоритме шифрования или сжатия имеется проблема.
Однако тот факт, что я не могу легко воспроизвести проблему, означает, что если есть ошибка, то это далеко не очевидно. Поэтому я размещаю код здесь, надеясь, что кто-то обнаружит что-то, чего я не видел.
Во-первых, блок AStream
данные сжимаются:
function CompressStream(AStream: TMemoryStream;
ACompressionLevel:
TCompressionLevel): Boolean;
var
LTempStream: TMemoryStream;
LCompressedStream: TCompressionStream;
begin
LTempStream := TMemoryStream.create;
try
try
AStream.Seek(0, soBeginning);
LCompressedStream := TCompressionStream.Create(ACompressionLevel, LTempStream);
try
try
LCompressedStream.CopyFrom(AStream, AStream.Size);
finally
LCompressedStream.free;
end;
finally
AStream.Clear;
AStream.CopyFrom(LTempStream, 0);
AStream.Seek(0, soBeginning);
Result := True;
end;
finally
LTempStream.free;
end;
except
Result := False;
end;
end;
Затем он зашифрован:
function EncryptStream(AStream: TMemoryStream; const AParameters: AnsiString): Boolean;
var
LModifiedStream: TMemoryStream;
LRijndaelCipher: TDCP_rijndael;
begin
try
LRijndaelCipher := TDCP_rijndael.Create(nil);
LModifiedStream := TMemoryStream.Create;
InitCipherWithKey(LRijndaelCipher, AParameters);
try
AStream.Seek(0, soBeginning);
LRijndaelCipher.EncryptStream(AStream, LModifiedStream, AStream.Size);
TMemoryStream(AStream).Clear;
AStream.CopyFrom(LModifiedStream, 0);
Result := True;
finally
LRijndaelCipher.Burn;
LRijndaelCipher.Free;
LModifiedStream.Free;
end;
except
Result := False;
end;
end;
... и где-то хранить.
При получении блока данных, это первый расшифрованы:
function DecryptStream(AStream: TMemoryStream; const AParameters: AnsiString): Boolean;
var
LModifiedStream: TMemoryStream;
LRijndaelCipher: TDCP_rijndael;
begin
try
LRijndaelCipher := TDCP_rijndael.Create(nil);
LModifiedStream := TMemoryStream.Create;
InitCipherWithKey(LRijndaelCipher, AParameters);
try
AStream.Seek(0, soBeginning);
LRijndaelCipher.DecryptStream(AStream, LModifiedStream, AStream.Size);
TMemoryStream(AStream).Clear;
AStream.CopyFrom(LModifiedStream, 0);
Result := True;
finally
LRijndaelCipher.Burn;
LRijndaelCipher.Free;
LModifiedStream.Free;
end;
except
Result := False;
end;
end;
Затем несжатый:
function DecompressStream(AStream: TMemoryStream): Boolean;
var
LTempStream: TMemoryStream;
LCompressedStream: TDecompressionStream;
begin
LTempStream := TMemoryStream.create;
try
try
AStream.Seek(0, soBeginning);
LCompressedStream := TDecompressionStream.Create(AStream);
try
try
LTempStream.CopyFrom(LCompressedStream, LCompressedStream.size);
finally
LCompressedStream.Free;
end;
finally
AStream.Clear;
AStream.CopyFrom(LTempStream, 0);
AStream.Seek(0, soBeginning);
Result := True;
end;
finally
LTempStream.free;
end;
except
Result := False;
end;
end;
Шифр инициализируется с помощью метода InitCipherWithKey()
. Он предназначен для преобразования хеша MD5 в его двоичное представление, содержащегося в переменной LMD5Hash
(да, массив имеет длину 64 байта, но только первый 16 будет использоваться шифром, так как я вызываю Init()
со значением 128 (что означает 128-бит/16 байт длина ключа):.
procedure InitCipherWithKey(ACipher: TDCP_cipher; const AKey: AnsiString);
var
LMD5Hash: array [0..63] of Byte;
S: AnsiString;
begin
//We use a 128 bit key
ZeroMemory(@LMD5Hash, SizeOf(LMD5Hash));
S := AKey;
HexToBin(PAnsiChar(S), LMD5Hash, Length(LMD5Hash) -1);
ACipher.Init(LMD5Hash[0], 128, nil);
ZeroMemory(@LMD5Hash, SizeOf(LMD5Hash));
end;
заранее спасибо
Большое спасибо за ваш ответ.InitCipherWithKey напрямую назначает двоичное значение шифру через функцию Init(). Нет никакого преобразования из двоичного в строку, а Init() требует двоичного буфера, поэтому я не уверен, что здесь что-то не так. Спасибо, что предложили использовать алгоритм PBKDF2, я это изучу! –
Распечатайте или зарегистрируйте в шестнадцатеричных (с использованием проверенного шестнадцатеричного кодировщика) все двоичные входы и выходы криптографических алгоритмов. Для исправления ошибки достаточно одного неправильного бита. –