5

我有一个在 Delphi 2007 中开发的应用程序,其中一个值由 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;
4

2 回答 2

10

该问题与字符编码有关,但并不是 DCPCrypt 无法处理 UTF-16。

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

由于显而易见的原因,这行代码在“unicode”(原文如此)编译器中也是错误的......

FillChar(Key[1],Length(Key),0);

边注

我是 TurboPower LockBox 3 的作者,我在论坛上遇到的第一个问题源于 ansistrings、utf-8 字符串和 utf-16le 字符串之间的混淆。很多人认为 PHP 理解的密码“abc”与 Delphi 2010 中的“abc”密码相同。密码库从密码字符串中使用的是字符串编码产生的二进制有效负载,而不是其语义作为字符串的意思。

于 2012-08-17T08:44:17.133 回答
5

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

如果其他人正在尝试类似的更新并需要解密 PHP 中生成的密文,我的解密函数和支持的 padWithZeros() 函数来自 DCPcrypt 站点,因此我将修改后的代码发布在这里。我也不应该在以下位置安装 2012 年 7 月 21 日更新到 DCPcrypt 的 XE2:http ://www.pepak.net/files/tools/dcpcrypt.zip 。

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;
于 2012-08-27T01:25:51.560 回答