【问题标题】:PHP to Delphi and back Encryption-Decryption using RijndaelPHP到Delphi并使用Rijndael返回加密-解密
【发布时间】:2011-10-26 12:05:25
【问题描述】:

我在使用 rijndael 密码解密从 PHP 发送到 Delphi 的字符串时遇到问题。 我在 PHP 端使用 mcrypt,在 Delphi 端使用 DCP_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;

我尝试在 Internet 上使用几个单元来实现密码,并发现大多数人都在谈论 DCP 组件。即便如此,我还没有设法让它正确解密。我已经尝试使用字节数组作为参数,AnsiStrings,WideStrings 等,但不幸的是没有运气。

如果我在这里遗漏了一些非常明显的东西,请原谅我,因为经过数小时的搜索,我的大脑状况不佳。

【问题讨论】:

  • 我建议在 PHP 和 Delphi 中同时实现加密和解密功能。然后您可以检查它们是否都生成相同的中间值,并且它们可以解密彼此的中间值。这应该可以缩小是否存在系统问题,或者特定功能是否存在错误。
  • nist.gov 网站和许多其他地方将有许多测试向量,您可以使用它们来查看这些实现是如何工作的。你德尔福函数对Data 的处理在我看来是错误的。 base64解码的结果应该是任意字节,而不是UTF8编码的字符串。
  • @GregS,我已经尝试过 base64 解码正常的非加密文本,效果很好。我不认为问题出在那儿。即使是这样,我也尝试了 2 个其他 Base64 实现,产生了相同的最终结果。
  • 我认为困扰@Greg 的不是Base64 解码,而是UTF8ToUnicodeString 调用。它将 Base64 解码结果视为 UTG8 字符串并尝试将其转换为 UTF16-LE。如果解码后的 Base64 中有任何字节将被视为 UTF-8 中的多字节字符,则您将更改长度(将 1+ 个字节转换为单个字符),从而更改您提供给 Rijndael 的数据.
  • 我只是把它改成了普通的Base64Decode。尽管如此,还是没有运气。我目前的猜测是这是一个填充问题,就像下面@rossum 所说的那样。

标签: php delphi encryption rijndael


【解决方案1】:

您的 PHP 和 Delphi 方法似乎都没有指定任何填充。如果默认填充不同,那么您将遇到问题。为两者明确指定 PKCS7(或 PKCS5)。

GregS 关于 Base64 解码结果的评论是正确的。您正在为您的 decRJ() 方法提供加密的密文。那将是随机出现的字节。尝试将其转换为 UTF-8 会破坏它,使其无法解密。传入的密文必须从 Base64 直接转换为字节数组。 Cyphertext 不是字符串,因此需要将其转换为 Base64 才能作为文本传输。它只会在被解密回明文之后再次成为文本。

【讨论】:

  • 我尝试了几种将 Base64 str 转换为各种数据类型以及字节数组的方法,但仍然没有运气。现在我检查了 PHP 演示 here 我发现连他的方式都行不通。诡异的。我不知道该怎么办。
  • 在 Delphi 中编写一个 encrypt() 方法并让它与您的 Delphi decRJ() 方法一起工作。
【解决方案2】:

我似乎在这方面花了太长时间,但是......

您的问题是块大小。 TDCP_rijndael 等效于 MCRYPT_RIJNDAEL_128(不是 _256)。 ciph.Init(...) 调用中的 '256' 值仍然是正确的。除此之外,它看起来还不错。也就是说,假设您将 ansistrings 用于 key/iv,或者您使用的是非 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; 

EDIT 2018(复活死者...):

根据要求,这里是编码方法,未经检查,直接从我找到的旧源文件中提取。

免责声明:它已经很多年了,在最近的记忆中未经测试,并且自 Delphi 2010 以来就没有使用过。现在可能有很多更好的选择。使用风险自负。

function Base64EncodeBytes(Input: TBytes): TBytes;
var
  ilen: integer;
begin
  ilen := Length(Input);
  SetLength(result, ((ilen + 2) div 3) * 4);
  Base64Encode(@Input[0], @result[0], ilen);
end;

【讨论】:

  • 我似乎在 Delphi 中找不到您的 Base64DecodeBytes 函数的参考。它是在 Delphi 的默认单位之一中声明的,还是您自己编写的函数?
  • 哎呀。我很久以前对 DCP 的东西进行了自己的修改,以使其能够识别 unicode,并添加了 Base64XXXBytes 的东西。编辑答案以添加(未选中)功能。
  • Base64DecodeBytes(输入:TBytes):TBytes;是越野车!!!它仅适用于要加密的短文本(有时会返回范围检查错误 crypt 或解码错误)请使用可以的 Soap.EncdDecd 函数。
  • @Evilripper:注意到了。我会相信你的话。但是,老实说,这个帖子已经有 5 年历史了,我已经毫无问题地通过了很多 MB,所以我现在会坚持下去。它适用于我们的情况,所以我不会弄乱它。我目前没有使用 Delphi,但如果我再次使用它,我会记住你的建议。实际上 - 我认为在随后的回答中我确实使用了 EncdDecd :-)
  • 您是否可以发布您对Base64EncodeBytes 的实现。那是缺失的
猜你喜欢
  • 1970-01-01
  • 2013-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-06
  • 2011-05-18
  • 2012-03-02
相关资源
最近更新 更多