Delphi DEC库(Rijndael)加密

发布于 2025-01-05 01:02:53 字数 1617 浏览 2 评论 0原文

我正在尝试使用 DEC 3.0 库 (Delphi 加密Compedium Part I)在 Delphi 7 中加密数据并通过 POST 将其发送到 PHP 脚本,我在其中使用 mcrypt 对其进行解密(RIJNDAEL_256,ECB 模式)。

Delphi 部分:

uses Windows, DECUtil, Cipher, Cipher1;

function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create(KeyStr, nil);
  RCipher.Mode:= cmECB;
  Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64);
  RCipher.Free;
end;

PHP 部分:

function decryptMsgContent($msgContent, $sKey) {
    return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND));
}

问题是 PHP 解密不起作用,输出是乱码,与实际数据不同。

当然,Delphi Key 和PHP $Key 是相同的 24 个字符的字符串。

现在我知道 DEC 3.0 已经过时了,而且我不是加密方面的专家,无法判断该实现是否实际上是 Rijndael 256。也许有人可以告诉我这个实现与 PHP 的 mcrypt w/ RIJNDAEL_256 有何不同。也许密钥大小不同,或者块大小不同,但无法从代码中看出这一点。以下是 Cipher1.pas 的摘录:

const
{ don’t change this }
  Rijndael_Blocks =  4;
  Rijndael_Rounds = 14;

class procedure TCipher_Rijndael.GetContext(var ABufSize, AKeySize, AUserSize: Integer);
begin
  ABufSize := Rijndael_Blocks * 4;
  AKeySize := 32;
  AUserSize := (Rijndael_Rounds + 1) * Rijndael_Blocks * SizeOf(Integer) * 2;
end;

附带问题:

我知道不推荐使用 ECB 模式,一旦 ECB 工作,我将立即使用 CBC。问题是,我是否也必须将 Delphi 中生成的 IV 传输到 PHP 脚本?或者知道密钥就足够了,就像欧洲央行那样?

I am trying to use the DEC 3.0 library (Delphi Encryption Compedium Part I) to encrypt data in Delphi 7 and send it to a PHP script through POST, where I am decrypting it with mcrypt (RIJNDAEL_256, ECB mode).

Delphi part:

uses Windows, DECUtil, Cipher, Cipher1;

function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create(KeyStr, nil);
  RCipher.Mode:= cmECB;
  Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64);
  RCipher.Free;
end;

PHP part:

function decryptMsgContent($msgContent, $sKey) {
    return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND));
}

The problem is that the decryption from PHP doesn't work and the output is gibberish, differing from the actual data.

Of course, Delphi Key and PHP $Key is the same 24 characters string.

Now I know DEC 3.0 is old and outdated, and I'm not an expert in encryption and can't tell if the inplementation is actually Rijndael 256. Maybe someone can tell me how this implementation differs from PHP's mcrypt w/ RIJNDAEL_256. Maybe the keysize is different, or the block size, but can't tell this from the code. Here's an excerpt from Cipher1.pas:

const
{ don’t change this }
  Rijndael_Blocks =  4;
  Rijndael_Rounds = 14;

class procedure TCipher_Rijndael.GetContext(var ABufSize, AKeySize, AUserSize: Integer);
begin
  ABufSize := Rijndael_Blocks * 4;
  AKeySize := 32;
  AUserSize := (Rijndael_Rounds + 1) * Rijndael_Blocks * SizeOf(Integer) * 2;
end;

Side question:

I know ECB mode isn't recommended and I'll use CBC as soon as I get ECB working. The question is, do I have to transmit the generated IV in Delphi to the PHP script also? Or knowing the key is sufficient, like for ECB?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

素手挽清风 2025-01-12 01:02:53

您正在调用 TCipher.Create(const Password: String; AProtection: TProtection);构造函数,它将在将密码传递给 Init 方法之前计算密码的哈希值,该方法执行所实现算法的标准密钥调度。要覆盖此密钥派生,请使用:

function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create('', nil);
  RCipher.Init(Pointer(Key)^,Length(Key),nil);
  RCipher.Mode:= cmECB;
  Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64);
  RCipher.Free;

end;

You are calling the TCipher.Create(const Password: String; AProtection: TProtection); constructor, which will compute a hash of the password before passing it to the Init method, which performs the standard key schedule of the implemented algorithm. To override this key derivation, use:

function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create('', nil);
  RCipher.Init(Pointer(Key)^,Length(Key),nil);
  RCipher.Mode:= cmECB;
  Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64);
  RCipher.Free;

end;

厌味 2025-01-12 01:02:53

好吧,总而言之,我的代码存在 3 个问题:

  1. 由于我对 mcrypt 和密码的一般理解很差,MCRYPT_RIJNDAEL_256 指的是 128 位,而不是指密钥大小。我的正确选择应该是 MCRYPT_RIJNDAEL_128,这是 AES 标准,也受到 DEC 3.0 的支持。

  2. DEC 有它自己的默认密钥派生,因此我需要绕过它,这样我就不必在 PHP 中实现它。实际上,我正在使用自己的密钥派生算法,该算法很容易在 PHP 中重现(sha1(key) 的前 32 个字符)。

  3. DEC 不会将明文填充到密码块大小的倍数,如 mcrypt 所期望的,因此我必须手动执行此操作。

提供以下工作代码:

Delphi:

uses Windows, DECUtil, Cipher, Cipher1, CryptoAPI;

function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
    KeyStr: string;
begin
  Result:= '';
  try
    // key derivation; just making sure to feed the cipher a 24 chars key
    HashStr(HASH_SHA1, Key, KeyStr);
    KeyStr:= Copy(KeyStr, 1, 24);
    RCipher:= TCipher_Rijndael.Create('', nil);
    RCipher.Init(Pointer(KeyStr)^, Length(KeyStr), nil);
    RCipher.Mode:= cmECB;
    Result:= RCipher.CodeString(MsgData + StringOfChar(#0,16-(Length(MsgData) mod 16)), paEncode, fmtMIME64);
    RCipher.Free;
  except
  end;
end;

PHP:

function decryptMsgContent($msgContent, $sKey) {
    $sKey = substr(sha1(sKey), 0, 24);
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}

OK, so to sum this up, there were 3 problems with my code:

  1. Due to my poor understanding of mcrypt and ciphers in general, MCRYPT_RIJNDAEL_256 refers to 128 bits block and doesn't refer to the keysize. My correct choice should have been MCRYPT_RIJNDAEL_128, which is the AES standard and is also supported by DEC 3.0.

  2. DEC has it's own default key derivation, so I needed to bypass it so I wouldn't have to implement it in PHP also. In actuality, I am using my own key derivation algorithm that was easy to reproduce in PHP (first 32 characters of sha1(key)).

  3. DEC doesn't pad plaintext to a multiple of the block size of the cipher, as mcrypt expects, so I had to do it manually.

Providing working code below:

Delphi:

uses Windows, DECUtil, Cipher, Cipher1, CryptoAPI;

function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
    KeyStr: string;
begin
  Result:= '';
  try
    // key derivation; just making sure to feed the cipher a 24 chars key
    HashStr(HASH_SHA1, Key, KeyStr);
    KeyStr:= Copy(KeyStr, 1, 24);
    RCipher:= TCipher_Rijndael.Create('', nil);
    RCipher.Init(Pointer(KeyStr)^, Length(KeyStr), nil);
    RCipher.Mode:= cmECB;
    Result:= RCipher.CodeString(MsgData + StringOfChar(#0,16-(Length(MsgData) mod 16)), paEncode, fmtMIME64);
    RCipher.Free;
  except
  end;
end;

PHP:

function decryptMsgContent($msgContent, $sKey) {
    $sKey = substr(sha1(sKey), 0, 24);
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}
泅渡 2025-01-12 01:02:53

我发现 256 位密钥有 32 个字符,即 32 个字节。不是 24。这可能就是问题所在。

[编辑]

我将每个人的想法(ansistring等)合并为一个想法并进行了修复。

另外,您正在使用 codestring( -- 它应该是 Encodestring(

我在下面粘贴了工作加密和解密源代码:


function EncryptMsgData(MsgData, Key: AnsiString): AnsiString;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create('', nil);
  RCipher.Init(Pointer(Key)^,Length(Key),nil);
  RCipher.Mode:= cmCBC;
  Result:= RCipher.EncodeString(MsgData);
  RCipher.Free;
end;

function DecryptMsgData(MsgData, Key: AnsiString): AnsiString;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create('',nil);
  RCipher.Init(Pointer(Key)^,Length(Key),nil);
  RCipher.Mode:= cmCBC;
  Result:= RCipher.DecodeString(MsgData);
  RCipher.Free;
end;

将其与 32 字符密钥一起使用,您将获得正确的加密和解密。

为了将加密数据存储和使用为字符串你可能想使用 Base64Encode(

但不要忘记在解密之前进行 Base64Decode。

这与 Blowfish 所需的技术相同。有时字符实际上就像一个退格键,执行该功能而不是Base64Encode 基本上将字符转换为可以以文本形式显示的内容,

然后通过互联网或相同或另一种语言将编码数据传输到其他应用程序,您必须进行 Base64 编码和解码,以免丢失数据。不要忘记 PHP 中也有它!

A 256 bit key I found is 32 charachters, or 32 bytes. Not 24. This may be the issue.

[EDIT]

I combined everyone's ideas (ansistring, etc) into one single idea with a fix.

Also, you are using codestring( -- it should be Encodestring(

I pasted working Encrypt and Decrypt source below:


function EncryptMsgData(MsgData, Key: AnsiString): AnsiString;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create('', nil);
  RCipher.Init(Pointer(Key)^,Length(Key),nil);
  RCipher.Mode:= cmCBC;
  Result:= RCipher.EncodeString(MsgData);
  RCipher.Free;
end;

function DecryptMsgData(MsgData, Key: AnsiString): AnsiString;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create('',nil);
  RCipher.Init(Pointer(Key)^,Length(Key),nil);
  RCipher.Mode:= cmCBC;
  Result:= RCipher.DecodeString(MsgData);
  RCipher.Free;
end;

Use that with a 32 charachter key and you get proper encryption and decryption.

In order to store and use the encrypted data as a string you may want to use Base64Encode(

But do not forget to Base64Decode prior to decrypting.

This is the same technique needed for Blowfish. Sometimes the charachters actually are like a backspace, and perform the function rather than showing on screen. Base64Encode basically converts the charachters to something you can display in text.

Prior to transferring the encoded data across the internet or to another application in the same or another language, you MUST base64encode and decode in order to not loose data. Don't forget it in PHP too!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文