我设计了一个存储数据块的系统,由多个客户端压缩和加密。
当尝试在特定计算机上获取存储数据块(我需要解压缩和解密)时,在数据解压缩期间会引发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个,因为我调用{{1}使用128值(表示128位/ 16字节密钥长度):
Init()
提前致谢。
答案 0 :(得分:1)
计算/运输钥匙时出现问题。首先,使用MD5从密码(错误地命名为AKey
)计算密钥。出于某种原因,您使用64字节的缓冲区,即使MD5总是输出16字节。这已经不安全了,您应该使用基于密码的密钥派生函数(如PBKDF2)从密码中派生密钥。
然而,您似乎将MD5的二进制输出视为字符。我假设你取InitCipherWithKey
的输出并将其放在AParameters
中。现在突然MD5的二进制输出被视为一个字符串。不幸的是,这意味着字节被解释为诸如控制字符之类的字符(如果字节具有值00
,则包括空终止字符值)。
因此,由于密钥本身丢失数据,解密很可能不会成功。这当然取决于关键值,使这个方案偶尔失败。请检查您的代码库,看看您是否在character-encoding和encoding上犯了错误。