2013-08-28 88 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

以十六進制(使用驗證的十六進制編碼器)打印或記錄加密算法的所有二進制輸入和輸出。一個不正確的位就足以使加密完全失敗。 –