2011-08-13 56 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