使用DCPcrypt的Delphi程序在升级到XE2后不会从PHP解密

使用DCPcrypt的Delphi程序在升级到XE2后不会从PHP解密,php,delphi,rijndael,Php,Delphi,Rijndael,我有一个在Delphi2007中开发的应用程序,其中一个值由PHP加密并在应用程序中解密。加密算法是RIJNDAEL 128。当我移动XE2并安装最新版本的DCPcrypt时,应用程序运行,但不再能够从PHP解密加密字符串。结果看起来像是汉字,所以我想知道是否需要修改对加密密钥、向量或加密字符串的处理,以说明XE2使用Unicode字符的事实 PHP加密由以下程序执行: (mcrypt_cbc(mcrypt_RIJNDAEL_128,$key,$date_str,mcrypt_ENCRYPT,$

我有一个在Delphi2007中开发的应用程序,其中一个值由PHP加密并在应用程序中解密。加密算法是RIJNDAEL 128。当我移动XE2并安装最新版本的DCPcrypt时,应用程序运行,但不再能够从PHP解密加密字符串。结果看起来像是汉字,所以我想知道是否需要修改对加密密钥、向量或加密字符串的处理,以说明XE2使用Unicode字符的事实

PHP加密由以下程序执行: (mcrypt_cbc(mcrypt_RIJNDAEL_128,$key,$date_str,mcrypt_ENCRYPT,$iv))

下面是两个相关的Delphi函数:

function PadWithZeros(const str : string; size : integer) : string;
var
  origsize, i : integer;
begin
  Result := str;
  origsize := Length(Result);
  if ((origsize mod size) <> 0) or (origsize = 0) then
  begin
    SetLength(Result,((origsize div size)+1)*size);
    for i := origsize+1 to Length(Result) do
      Result[i] := #0;
  end;
end;

procedure TfrmMain.btnDecryptClick(Sender: TObject);
var
  Cipher : TDCP_rijndael;
  Data, Key, IV : string;
begin
  // Pad Key and IV with zeros as appropriate
  Key := PadWithZeros(boxKey.Text,KeySize);
  IV := PadWithZeros(boxIV.Text,BlockSize);
  // Decode the Base64 encoded string
  Data := Base64DecodeStr(boxCipherTextIn.Text);
  // Create the cipher and initialise according to the key length
  Cipher := TDCP_rijndael.Create(Self);
  if Length(boxKey.Text) <= 16 then
    Cipher.Init(Key[1],128,@IV[1])
  else if Length(boxKey.Text) <= 24 then
    Cipher.Init(Key[1],192,@IV[1])
  else
    Cipher.Init(Key[1],256,@IV[1]);
  // Decrypt the data
  Cipher.DecryptCBC(Data[1],Data[1],Length(Data));
  // Free the cipher and clear sensitive information
  Cipher.Free;
  FillChar(Key[1],Length(Key),0);
  // Display the result
  boxPlainTextOut.Text := Data;
end;
函数PadWithZeros(const str:string;size:integer):string;
变量
origsize,i:整数;
开始
结果:=str;
origsize:=长度(结果);
如果((origsize mod size)0)或(origsize=0),则
开始
SetLength(结果,((origsize div size)+1)*size);
对于i:=origsize+1到长度(结果)do
结果[i]:=#0;
结束;
结束;
过程TfrmMain.btnDecryptClick(发送方:TObject);
变量
密码:TDCP_rijndael;
数据,键,IV:字符串;
开始
//根据需要,将键盘和IV用零填充
键:=带零的焊盘(boxKey.Text,KeySize);
IV:=带零的焊盘(boxIV.文本,块大小);
//解码Base64编码的字符串
数据:=Base64DecodeStr(boxciphertemine.Text);
//创建密码并根据密钥长度初始化
密码:=TDCP_rijndael.Create(Self);

如果长度(boxKey.Text)问题与字符编码有关,但并非DCPCrypt无法处理UTF-16

PHP字符串是UTF-8。因此,您将密码(“Key”)作为base64编码字符串从PHP传递给Dephi。奇怪的是,您将解码的密钥存储在UTF-16LE字符串中。更合适的是rawbytestring、TBytes或TMemoryStream。密钥有效负载的二进制布局现在与编码端不同,因为它被键入UTF16-LE(在不正确的Delphi术语中,“unicode字符串”-微软和Embarcadero malapropism)

同样,这行代码在“unicode”(sic)编译器中是错误的,原因很明显

FillChar(Key[1],Length(Key),0);
旁注
我是TurboPower LockBox 3的作者,我在论坛上得到的第一个问题来自于Ansistring、utf-8字符串和utf-16le字符串之间的混淆。很多人认为PHP所理解的密码“abc”与Delphi2010中的“abc”相同。密码库从密码字符串中使用的是编码字符串所产生的二进制负载,而不是字符串的语义。

应用程序现在可以在XE2中正确编译,并且能够解密使用Delphi 2007编译的版本中的记录。谢谢大家的评论。不同的透视图有助于明确何时需要将变量定义为ANSI字符串

如果其他人正在尝试类似的更新,并且需要解密PHP中生成的密码文本,我的解密函数和支持的padWithZeros()函数来自DCPcrypt站点,因此我将修改后的代码发布在这里。我也不认为我在以下位置安装了2012年7月21日DCPcrypt for XE2的更新:

函数TForm1.aesdefcrypt(const DecKeyStr:AnsiString; const DecIVStr:AnsiString; 常数cyphertemine:ansisting):字符串; 变量 密码:TDCP_rijndael; 数据,键,IV:解析; 开始 //根据需要,将键盘和IV用零填充 键:=带零的焊盘(DecKeyStr,键大小); IV:=带零的焊盘(分贝,块大小); //解码Base64编码的字符串 数据:=Base64DecodeStr(cypherteme); //创建密码并根据密钥长度初始化 密码:=TDCP_rijndael.Create(nil);
如果Length(DecKeyStr)在D2007中起作用,并在升级到XE2时停止,那么问题显然与Ansi/Unicode有关。在Delphi代码中将引用从
string
更改为
AnsiString
,看看这是否解决了问题。不一定是Ken。在这种情况下,很可能是这样,但是仅仅因为D2009中开始出现问题就自动处理“Unicode问题”是一个错误。我在D2009中遇到了一个问题,这似乎是由于编译器处理外部API函数decl中声明为错误大小的参数中剩余字节的方式发生了变化+“加密库从密码字符串中使用的是由字符串编码产生的二进制有效负载,而不是字符串的语义。”我知道的几乎每个人都认为加密是关于文本的,但它只是关于二进制的。将文本转换为二进制并返回不是加密库的任务。@JeroenviertPluimers-没错,但作者库现在应该已经学会了提供帮助器重载方法:-)加密时,锁盒3自动生成随机IV。IV的低64位为nonce。该nonce在“清除到密文”中提前。解密时,锁盒3假定头64位是用于生成该消息的IV的nonce。我对PHP或它的加密库一无所知,所以我不能断言与LockBox 3的互操作性。我的建议是使用密钥流链接模式。这将消除任何块量化问题。
KeySize
BlockSize
的值是多少?
function TForm1.AESDecrypt(const DecKeyStr: AnsiString;
                              const DecIVStr: AnsiString;
                              const CypherTextIn: AnsiString): String;
var
  Cipher : TDCP_rijndael;
  Data, Key, IV : AnsiString;
begin
  // Pad Key and IV with zeros as appropriate
  Key := PadWithZeros(DecKeyStr,KeySize);
  IV := PadWithZeros(DecIVStr,BlockSize);
  // Decode the Base64 encoded string
  Data := Base64DecodeStr(CypherTextIn);
  // Create the cipher and initialise according to the key length
  Cipher := TDCP_rijndael.Create(nil);
  if Length(DecKeyStr) <= 16 then
    Cipher.Init(Key[1],128,@IV[1])
  else if Length(DecKeyStr) <= 24 then
    Cipher.Init(Key[1],192,@IV[1])
  else
    Cipher.Init(Key[1],256,@IV[1]);
  // Decrypt the data
  Cipher.DecryptCBC(Data[1],Data[1],Length(Data));
  // Free the cipher and clear sensitive information
  Cipher.Free;
  FillChar(Key[1],Length(Key),0);
  // Display the result
  Result := String(Data);
(* *)
end;

function TForm1.PadWithZeros(const str:AnsiString; size:integer):AnsiString;
var
  origsize, i : integer;
begin
  Result := str;
  origsize := Length(Result);
  if ((origsize mod size) <> 0) or (origsize = 0) then
  begin
    SetLength(Result,((origsize div size)+1)*size);
    for i := origsize+1 to Length(Result) do
      Result[i] := #0;
  end;
end;