2012-02-10 110 views
7

我試圖使用DEC 3.0庫Delphi Encryption Compedium Part I)在Delphi 7中的數據進行加密,並通過POST,在那裏我與mcrypt的解密它(發送到PHP腳本RIJNDAEL_256,ECB模式)。德爾福DEC庫(Rijndael算法)加密

德爾福部分:

uses Windows, DECUtil, Cipher, Cipher1; 

function EncryptMsgData(MsgData, Key: string): string; 
var RCipher: TCipher_Rijndael; 
begin 
    RCipher:= TCipher_Rijndael.Create(KeyStr, nil); 
    RCipher.Mode:= cmECB; 
    Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64); 
    RCipher.Free; 
end; 

PHP部分:

function decryptMsgContent($msgContent, $sKey) { 
    return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)); 
} 

的問題是,從PHP解密無法正常工作,輸出爲亂碼,從實際的數據不同。

當然,德爾福Key和PHP $Key是相同的24個字符的字符串。

現在我知道DEC 3.0是陳舊過時的,我不是加密專家,也不能確定實際上是否是Rijndael 256.也許有人可以告訴我這個實現與PHP的mcrypt w/RIJNDAEL_256。也許密鑰大小不同,或塊大小,但不能從代碼中告訴它。下面是Cipher1.pas的摘錄:

const 
{ don’t change this } 
    Rijndael_Blocks = 4; 
    Rijndael_Rounds = 14; 

class procedure TCipher_Rijndael.GetContext(var ABufSize, AKeySize, AUserSize: Integer); 
begin 
    ABufSize := Rijndael_Blocks * 4; 
    AKeySize := 32; 
    AUserSize := (Rijndael_Rounds + 1) * Rijndael_Blocks * SizeOf(Integer) * 2; 
end; 

方問題:

我知道不建議ECB模式,我得到歐洲央行的工作我會盡快使用CBC。問題是,我是否必須將Delphi中生成的IV傳遞給PHP腳本?或者知道關鍵是足夠的,就像歐洲央行?

+0

這可能是一個非常愚蠢的問題。但是使用delphi可以解密你的加密數據嗎?哦,並且這個問題的答案是否有幫助:http://stackoverflow.com/q/8313992/41338 – RobS 2012-02-10 10:57:43

+0

您可以調用mcrypt_create_iv()。 Delphi中使用的IV是什麼? – 2012-02-10 11:14:50

+0

@ldsandon:talereader正在使用ECB模式。沒有IV。 – 2012-02-10 11:26:00

回答

6

您正在調用TCipher.Create(const Password:String; AProtection:TProtection);構造函數,在將密碼傳遞給Init方法之前計算密碼的哈希值,Init方法執行所實現的算法的標準密鑰調度。要覆蓋此密鑰推導,請使用:

function EncryptMsgData(MsgData, Key: string): string; 
var RCipher: TCipher_Rijndael; 
begin 
    RCipher:= TCipher_Rijndael.Create('', nil); 
    RCipher.Init(Pointer(Key)^,Length(Key),nil); 
    RCipher.Mode:= cmECB; 
    Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64); 
    RCipher.Free; 

end;

+0

謝謝,這讓我走上了正軌。我實際上使用我自己的密碼轉換(從發佈的代碼中省略),它只接受PasswordSecret + Timestamp的sha1的前24個字符(即POST數據中的前兩個字符)。當我在PHP中遇到錯誤時,我不得不這樣做,因爲mcrypt接受了最多24個字符的密鑰。 – talereader 2012-02-10 12:32:30

+0

問題的另一部分是DEC實現實際上是** 128位** Rijndael。發現通過更改PHP解密函數來使用RIJNDAEL_128。現在PHP解密函數的輸出結尾有原始數據+一些亂碼。從我之前在其他文章中看到的stackoverflow中,有一個填充問題在這裏,我必須弄清楚。 – talereader 2012-02-10 12:40:16

+0

此外,通過查看DEC源,是否可以進行一些修改以將其轉換爲128位塊大小和256(AES 256)密鑰大小的變體,因此它將與mcrypt和RIJNDAEL_256 ? – talereader 2012-02-10 12:45:23

2

好了,總結這件事,有3個問題與我的代碼:

  1. 由於我的mcrypt和一般密碼的理解不夠,MCRYPT_RIJNDAEL_256是指128位並沒有按」 t引用密鑰大小。我的正確選擇應該是MCRYPT_RIJNDAEL_128,這是AES標準,也是DEC 3.0支持的。

  2. DEC有它自己的默認密鑰派生,所以我需要繞過它,所以我不必在PHP中也實現它。實際上,我使用自己的密鑰派生算法,它很容易在PHP中重現(sha1(key)的前32個字符)。

  3. DEC不會將明文填充到密碼塊大小的倍數,因爲mcrypt需要,所以我必須手動完成。

下面提供的工作代碼:

的Delphi:

uses Windows, DECUtil, Cipher, Cipher1, CryptoAPI; 

function EncryptMsgData(MsgData, Key: string): string; 
var RCipher: TCipher_Rijndael; 
    KeyStr: string; 
begin 
    Result:= ''; 
    try 
    // key derivation; just making sure to feed the cipher a 24 chars key 
    HashStr(HASH_SHA1, Key, KeyStr); 
    KeyStr:= Copy(KeyStr, 1, 24); 
    RCipher:= TCipher_Rijndael.Create('', nil); 
    RCipher.Init(Pointer(KeyStr)^, Length(KeyStr), nil); 
    RCipher.Mode:= cmECB; 
    Result:= RCipher.CodeString(MsgData + StringOfChar(#0,16-(Length(MsgData) mod 16)), paEncode, fmtMIME64); 
    RCipher.Free; 
    except 
    end; 
end; 

PHP:

function decryptMsgContent($msgContent, $sKey) { 
    $sKey = substr(sha1(sKey), 0, 24); 
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND))); 
} 
+0

嚴。 SHA-1將只輸出20個字節。最簡單的解決方案可能是將密鑰大小減少到128位/ 16字節。另一種方法是使用SHA-256並從DEC 3.0切換到另一個支持SHA-256的庫,例如<無恥插件> OpenStrSecII或StreamSec Tools或DCP Crypt。第三種方法是使用帶有密鑰擴展的SHA-1,但這很容易在PHP中實現起來很複雜,DEC 3.0沒有實現任何PKCS#5 v2.0功能。 – 2012-02-11 10:41:22

+0

我是否正確地說,上面的代碼實際上使用AES-128,而不是192,即使密鑰的長度是24個字符?我應該把de鍵長度減少到16嗎?因爲我暫時堅持使用AES-128,這是一個小項目。此外,我切換到CBC,讓DEC在內部生成IV,並且我只通過IV base64編碼和數據。 – talereader 2012-02-12 09:51:26

+0

它使用AES-192,但密鑰中最多有160位熵(由SHA-1的輸出長度限定),並且密鑰的最後4個字節(#21到#24)未定義(儘管大概是0,因爲你得到它的工作)。 – 2012-02-12 11:20:26

0

A I發現256位密鑰是32個charachters,或者32個字節。不是24.這可能是問題。

[編輯]

我結合大家的想法(AnsiString類型等),並在修復一個單一的想法。

此外,您使用的代碼串( - 應該Encodestring(

我粘貼下面的工作加密和解密來源:


function EncryptMsgData(MsgData, Key: AnsiString): AnsiString; 
var RCipher: TCipher_Rijndael; 
begin 
    RCipher:= TCipher_Rijndael.Create('', nil); 
    RCipher.Init(Pointer(Key)^,Length(Key),nil); 
    RCipher.Mode:= cmCBC; 
    Result:= RCipher.EncodeString(MsgData); 
    RCipher.Free; 
end; 

function DecryptMsgData(MsgData, Key: AnsiString): AnsiString; 
var RCipher: TCipher_Rijndael; 
begin 
    RCipher:= TCipher_Rijndael.Create('',nil); 
    RCipher.Init(Pointer(Key)^,Length(Key),nil); 
    RCipher.Mode:= cmCBC; 
    Result:= RCipher.DecodeString(MsgData); 
    RCipher.Free; 
end; 

使用了32 charachter密鑰,你會得到正確的加密和解密。

爲了存儲和使用加密將數據作爲一個字符串,你可能想要使用Base64Encode(

但是在解密之前不要忘記Base64Decode。

這與Blowfish所需的技術相同。有時候,這些字符實際上就像一個退格鍵,並且執行該功能而不是在屏幕上顯示。 Base64Encode基本上將字符轉換爲可以在文本中顯示的東西。

在將編碼數據通過互聯網或以相同或另一種語言傳輸到其他應用程序之前,您必須進行64位編碼和解碼,以便不會丟失數據。不要忘記它在PHP中!