2013-08-28 6 views
1

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

заранее спасибо

ответ

1

Существует что-то пойдет не так при расчете/транспортировки ключа Сначала вы вычислить ключ из пароля (ошибочно называемого AKey.) с использованием MD5. По какой-то причине вы используете для этого буфер из 64 байтов, хотя MD5 всегда выводит ровно 16 байтов. Это уже не безопасно, вы должны использовать основанная на пароле функция вывода ключей, такая как PBKDF2 для получения ключей от паролей.

Затем, однако, вы рассматриваете двоичный вывод MD5 как символы. Я предполагаю, что вы делаете вывод InitCipherWithKey и помещаете его в AParameters. Теперь вдруг двоичный вывод MD5 рассматривается как строка. К сожалению, это означает, что байты интерпретируются как символы, такие как управляющие символы (включая значение символа нулевого окончания, если байт имеет значение 00).

Так что, скорее всего, дешифрование не будет выполнено из-за потери данных из самого ключа. Это, конечно, зависит от ключевого значения, что делает эту схему неудачной и сейчас. Пожалуйста, проверьте свою базу кода, чтобы убедиться, что вы делаете ошибки относительно и .

+0

Большое спасибо за ваш ответ.InitCipherWithKey напрямую назначает двоичное значение шифру через функцию Init(). Нет никакого преобразования из двоичного в строку, а Init() требует двоичного буфера, поэтому я не уверен, что здесь что-то не так. Спасибо, что предложили использовать алгоритм PBKDF2, я это изучу! –

+0

Распечатайте или зарегистрируйте в шестнадцатеричных (с использованием проверенного шестнадцатеричного кодировщика) все двоичные входы и выходы криптографических алгоритмов. Для исправления ошибки достаточно одного неправильного бита. –