use*_*791 4 php delphi rijndael
我有一个在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;
Run Code Online (Sandbox Code Playgroud)
该问题与字符编码有关,但并不是DCPCrypt无法处理UTF-16.
PHP字符串是UTF-8.因此,您将密码('Key')从PHP传递给Dephi作为base64编码的字符串.奇怪的是,您将解码的密钥存储在UTF-16LE字符串中.更合适的是rawbytestring或TBytes或TMemoryStream.Key的有效载荷的二进制布局现在与编码方面的二进制布局不同,因为它被键入为UTF16-LE(在不正确的Delphi术语中,'unicode字符串' - 微软和Embarcadero malapropism).
由于显而易见的原因,这行代码在'unicode'(原文如此)编译器中是错误的...
FillChar(Key[1],Length(Key),0);
Run Code Online (Sandbox Code Playgroud)
我是TurboPower LockBox 3的作者,我在论坛中遇到的头号问题源于ansistrings,utf-8字符串和utf-16le字符串之间的混淆.很多人认为PHP理解密码'abc',与Delphi 2010中的'abc'密码相同.密码库使用的密码字符串是由字符串编码产生的二进制有效负载,而不是其语义意思是一个字符串.
该应用程序现在可以在 XE2 中正确编译,并且能够从 Delphi 2007 编译的版本中解密记录。感谢大家的意见。不同的观点有助于明确何时需要将变量定义为 ANSI 字符串。
如果其他人正在尝试类似的更新并需要解密在 PHP 中生成的密文,我的解密函数和支持的 padWithZeros() 函数来自 DCPcrypt 站点,所以我在这里发布我修改过的代码。我也不应该说我为 XE2 安装了 7/21/2012 的 DCPcrypt 更新,该更新可在以下位置获得: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;
Run Code Online (Sandbox Code Playgroud)