2011-08-13 55 views
2

我有解密使用rijndael密碼從PHP發送到德爾福的字符串的問題。 我在PHP端使用mcrypt,在Delphi端使用DCP_rijndael。PHP到德爾福和回加密解密使用Rijndael

目前我有下面的代碼。

PHP:

function encRJ($key, $iv, $data) 
{ 
    $r = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv); 
    $r = base64_encode($r); 
    return $r; 
} 

而在德爾福:

function decRJ(Data: string; Key: string; IV: string): string; 
var ciph: TDCP_rijndael; 
begin 
    Data := Base64DecodeStr(Data); 

    ciph:= TDCP_rijndael.Create(Self); 
    ciph.Init(Key[1], 256, @IV[1]); 
    ciph.DecryptCBC(Data[1], Data[1], Length(Data)); 
    ciph.Free; 

    Result := Data; 
end; 

我已經利用互聯網實施的密碼在幾個單位審判,發現大多數人都在說關於DCP組件。即便如此,我還沒有設法正確解密。我已經嘗試使用Byte數組作爲參數,AnsiStrings,WideStrings等,但不幸的是沒有運氣。

對不起,如果我在這裏丟失了一些非常明顯的東西,因爲我的思想不夠好,經過幾個小時尋找問題。

+5

我建議實行兩種加密和PHP和德爾福解密功能。然後您可以檢查它們是否都生成相同的中間值,並且可以解密彼此的中間值。這應該縮小是否存在系統性問題,或者是否存在特定功能的錯誤。 – Gus

+2

nist.gov網站和許多其他地方將有許多測試向量,您可以使用它們來了解這些實現如何工作。 Delphi函數對'Data'的處理對我來說看起來是錯誤的。 base64解碼的結果應該是任意字節,而不是UTF8編碼的字符串。 –

+0

@GregS,我試過base64解碼正常,非加密文本,它沒有問題。我認爲這個問題並不存在。即使這樣做,我已經嘗試了2個其他的Base64實現,並得到了相同的最終結果。 – Josh

回答

0

您的PHP或您的Delphi方法都不會指定任何填充。如果默認填充不同,那麼你會遇到問題。爲兩者明確指定PKCS7(或PKCS5)。

GregS對解碼Base64結果的評論是正確的。您正在向decRJ()方法提供加密的密文。這將是隨機出現的字節。試圖將其轉換爲UTF-8將破壞它,因此無法解密。傳入的密文必須從Base64直接轉換爲字節數組。連字符不是字符串,這就是爲什麼它需要轉換爲Base64以作爲文本傳輸。它只會在之後再次被文本解密回明文。

+0

我已經嘗試了幾種將Base64 str轉換爲各種數據類型以及字節數組的方式,但仍然沒有運氣。現在我檢查了PHP演示[這裏](http://www.cityinthesky.co.uk/opensource/DCPcrypt)我剛發現,甚至連他的方式都行不通。奇怪的。我不知道該怎麼做atm。 – Josh

+1

在Delphi中編寫'encrypt()'方法,並使用你的Delphi'decRJ()'方法處理它。 – rossum

7

我似乎花了太多時間在這個,但...

您的問題是塊大小。 TDCP_rijndael等同於MCRYPT_RIJNDAEL_128(不是_256)。 ciph.Init(...)調用中的'256'值仍然正確。除此之外,它看起來非常好。也就是說,假設您使用的是鍵/ iv的ansistrings,或者您使用的是非unicode Delphi。
對於unicode的Delphi版本,我傾向於使用TBytes和key [0]/iv [0]。

填充可能仍然是一個問題。如果是這樣,那麼這裏就是我基於PHP手冊頁和一些試驗和錯誤而改變的。

PHP:

function Encrypt($src, $key, $iv) 
{ 
    $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc'); 
    //echo "Block size: " . $block . "\r\n"; 
    $pad = $block - (strlen($src) % $block); 
    $src .= str_repeat(chr($pad), $pad); 

    $enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $src, MCRYPT_MODE_CBC, $iv); 
    $r = base64_encode($enc); 
    return $r; 
} 

function Decrypt($src, $key, $iv) 
{ 
    $enc = base64_decode($src); 
    $dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $enc, MCRYPT_MODE_CBC, $iv); 

    $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc'); 
    $pad = ord($dec[($len = strlen($dec)) - 1]); 
    return substr($dec, 0, strlen($dec) - $pad); 
} 

德爾福:

function DecryptData(Data: string; AKey: AnsiString; AIv: AnsiString): string; 
var 
    key, iv, src, dest: TBytes; 
    cipher: TDCP_rijndael; 
    slen, pad: integer; 
begin 
    //key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey)); 
    //iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv)); 
    key := TEncoding.ASCII.GetBytes(AKey); 
    iv := TEncoding.ASCII.GetBytes(AIv); 

    src := Base64DecodeBytes(TEncoding.UTF8.GetBytes(Data)); 

    cipher := TDCP_rijndael.Create(nil); 
    try 
    cipher.CipherMode := cmCBC; 
    slen := Length(src); 
    SetLength(dest, slen); 
    cipher.Init(key[0], 256, @iv[0]); // DCP uses key size in BITS not BYTES 
    cipher.Decrypt(src[0], dest[0], slen); 
    // Remove the padding. Get the numerical value of the last byte and remove 
    // that number of bytes 
    pad := dest[slen - 1]; 
    SetLength(dest, slen - pad); 

    // Base64 encode it 
    result := TEncoding.Default.GetString(dest); 
    finally 
    cipher.Free; 
    end; 
end; 

function EncryptData(Data: string; AKey: AnsiString; AIv: AnsiString): string; 
var 
    cipher: TDCP_rijndael; 
    key, iv, src, dest, b64: TBytes; 
    index, slen, bsize, pad: integer; 
begin 
    //key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey)); 
    //iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv)); 
    key := TEncoding.ASCII.GetBytes(AKey); 
    iv := TEncoding.ASCII.GetBytes(AIv); 

    src := TEncoding.UTF8.GetBytes(Data); 

    cipher := TDCP_rijndael.Create(nil); 
    try 
    cipher.CipherMode := cmCBC; 
    // Add padding. 
    // Resize the Value array to make it a multiple of the block length. 
    // If it's already an exact multiple then add a full block of padding. 
    slen := Length(src); 
    bsize := (cipher.BlockSize div 8); 
    pad := bsize - (slen mod bsize); 
    Inc(slen, pad); 
    SetLength(src, slen); 
    for index := pad downto 1 do 
    begin 
     src[slen - index] := pad; 
    end; 

    SetLength(dest, slen); 
    cipher.Init(key[0], 256, @iv[0]); // DCP uses key size in BITS not BYTES 
    cipher.Encrypt(src[0], dest[0], slen); 

    b64 := Base64EncodeBytes(dest); 
    result := TEncoding.Default.GetString(b64); 
    finally 
    cipher.Free; 
    end; 
end; 

的PHP和Delphi功能現在給我相同的答案。

編輯

Base64DecodeBytes有點的代碼,我加入到DCP Base64編碼單元:

function Base64DecodeBytes(Input: TBytes): TBytes; 
var 
    ilen, rlen: integer; 
begin 
    ilen := Length(Input); 
    SetLength(result, (ilen div 4) * 3); 
    rlen := Base64Decode(@Input[0], @result[0], ilen); 
    // Adjust the length of the output buffer according to the number of valid 
    // b64 characters 
    SetLength(result, rlen); 
end; 
+0

我似乎無法在Delphi中找到Base64DecodeBytes函數的參考。它是在Delphi的默認單元之一中聲明的,還是它是你自己編寫的函數? – Josh

+0

哎呦。我很早以前就對DCP的東西做了自己的修改,以使其能夠識別unicode並添加了Base64XXXBytes。編輯答案以添加(未選中)功能。 – shunty

+0

Base64DecodeBytes(Input:TBytes):TBytes;是越野車!它只能用短文本進行加密(有時會返回範圍檢查錯誤crypt或解碼錯誤),請使用Soap.EncdDecd函數。 – Evilripper