2013-08-28 89 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个将被密码使用,因为我用128值调用Init()(这意味着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计算的关键。 )由于某种原因,你使用了一个64字节的缓冲区,即使MD5总是输出16个字节,这已经不安全了,你应该使用基于密码的密钥派生函数(如PBKDF2)从密码派生密钥。

然而,你似乎把MD5的二进制输出视为字符。我认为你把InitCipherWithKey的输出放在AParameters。现在突然MD5的二进制输出被视为一个字符串。不幸的是,这意味着字节被解释为诸如控制字符的字符(如果字节的值为00,则包括空终止字符值)。

因此,它是最有可能的是,解密不会因为键本身的数据丢失的成功。这当然取决于关键的价值,使得这个计划不时地失败。请检查您的代码库,看看您是否对发生了错误。

+0

非常感谢您的回答。InitCipherWithKey通过Init()函数直接将二进制值分配给密码。没有从二进制到字符串的转换,并且Init()需要二进制缓冲区,所以我不确定这里有什么问题。感谢您提供使用PBKDF2算法的建议,我会研究它! –

+0

以十六进制(使用验证的十六进制编码器)打印或记录加密算法的所有二进制输入和输出。一个不正确的位就足以使加密完全失败。 –