Php Delphi DEC库(Rijndael)加密

Php Delphi DEC库(Rijndael)加密,php,delphi,delphi-7,mcrypt,rijndael,Php,Delphi,Delphi 7,Mcrypt,Rijndael,我试图使用DEC 3.0库()在Delphi 7中加密数据,并通过POST将其发送到PHP脚本,在那里我使用mcrypt(RIJNDAEL_256,ECB模式)对其进行解密 德尔福部分: uses Windows, DECUtil, Cipher, Cipher1; function EncryptMsgData(MsgData, Key: string): string; var RCipher: TCipher_Rijndael; begin RCipher:= TCipher_Rij

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

德尔福部分:

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/rijndael256有什么不同。可能键大小不同,或者块大小不同,但无法从代码中区分这一点。以下是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脚本?或者知道密钥就足够了,比如ECB?

您正在调用TCipher.Create(const Password:String;AProtection:TProtection);构造函数,它将在将密码传递给Init方法之前计算其哈希值,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;

结束

好的,总而言之,我的代码有3个问题:

  • 由于我对mcrypt和密码的总体理解较差,mcrypt_RIJNDAEL_256表示128位,而不表示密钥大小。我正确的选择应该是MCRYPT_RIJNDAEL_128,这是AES标准,也是DEC 3.0支持的

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

  • DEC并不像mcrypt所期望的那个样,将明文填充到密码块大小的倍数,所以我必须手动完成

  • 提供以下工作代码:

    德尔菲:

    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:


    我找到的256位密钥是32个字符或32个字节。不是24岁。这可能就是问题所在

    [编辑]

    我把每个人的想法(翻译等)结合成一个想法,并加以修正

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

    我将工作加密和解密源粘贴到下面:



    使用一个32字符的密钥,你可以得到正确的加密和解密

    为了将加密数据作为字符串存储和使用,您可能需要使用Base64Encode(

    但不要忘记在解密之前解码

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


    在通过internet或使用相同或其他语言将编码数据传输到另一个应用程序之前,您必须进行Base64编码和解码,以避免数据丢失。在PHP中也不要忘记这一点!

    这可能是一个非常愚蠢的问题。但是使用delphi您能够解密加密数据吗?哦,这个问题的答案是关于帮助:你调用mcrypt_create_iv()。你在Delphi中使用的iv是什么?@ldsandon:talereader使用ECB模式。没有iv。希望PHP知道这一点-不知道调用mcrypt_create_iv(mcrypt_get_iv_size(mcrypt_RIJNDAEL_256,mcrypt_mode_ECB),mcrypt_RAND)会发生什么情况;也许它只是被忽略了(我希望),可能它触发了一些不好的东西。如果它返回False,它可能会将一个不好的参数传递给mcrypt_decrypt。@Isandon想到了这一点,在PHP中通过在加密和解密中生成IV来测试加密/解密,并且输出是正常的。所以看起来在ECB中,mcrypt忽略了传递的IV。谢谢,这让我走上了正确的轨道。我真的很好y使用我自己的密码转换(从发布的代码中省略),该转换只使用PasswordSecret+Timestamp的sha1的前24个字符(在发布数据中进行)。当我在PHP中遇到一个错误,即mcrypt接受最多24个字符的密钥时,我不得不这样做。问题的另一部分是DEC实现实际上是128位的Rijndael。发现通过将PHP解密函数更改为使用Rijndael_128。现在PHP解密函数的输出具有原始数据+一些乱码最后是sh。从我之前在stackoverflow的其他帖子中读到的内容来看,这里有一个填充问题,我必须解决。对,DEC不会在ECB模式下填充纯文本,这意味着传递给函数的纯文本的长度必须是密码块大小的倍数PHP默认情况下使用零填充,因此您应该使用StringOfChar(#0,16-(长度(MsgData)mod 16))填充MsgData,假设您使用的是非unicode版本的Delphi.DEC实现的Rijndael的块大小为128位,密钥大小为128、192和256位。如果传递给Init方法的密钥的长度符合可用文档MCRYPT_Rijndael_128对应的AES(Rijndael的块大小为128位),不考虑密钥大小。MCRYPT_RIJNDAEL_256不是AES,DEC.Uhm不支持。SHA-1将只输出20字节。最简单的解决方案可能是将密钥大小减少到128位/16字节。另一种选择是使用SHA-256,并从DEC 3.0切换到支持SHA-256的另一个库,如OpenSTRESCII或StreamSec工具或者DCP Crypt。第三种选择是使用SHA-1和密钥拉伸,但这很容易在PHP中实现,而且DEC 3.0没有实现任何PKCS#5 v2.0函数。我说的cod是正确的吗
    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)));
    }
    
    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;