Delphi7-使用DEC加密,使用PHP OpenSSL解密

Delphi7-使用DEC加密,使用PHP OpenSSL解密,delphi,encryption,delphi-7,encryption-symmetric,php-openssl,Delphi,Encryption,Delphi 7,Encryption Symmetric,Php Openssl,使用:Delphi7,DEC v5.2 请参阅以下问题: 从@AmigoJack的优秀答案来看,我的Delphi解密函数运行良好。基于此,我现在正在尝试实现加密功能,但到目前为止没有成功。所发生的事情是,加密是在Delphi中完成的,而在PHP中解密时生成的字符串与加密后的字符串不同,这意味着Delphi代码中存在错误 代码如下: uses SysUtils, Windows, Classes, DECCipher, DECFmt, DecUtil; function Encrypt(AStr

使用:Delphi7,DEC v5.2

请参阅以下问题:

从@AmigoJack的优秀答案来看,我的Delphi解密函数运行良好。基于此,我现在正在尝试实现加密功能,但到目前为止没有成功。所发生的事情是,加密是在Delphi中完成的,而在PHP中解密时生成的字符串与加密后的字符串不同,这意味着Delphi代码中存在错误

代码如下:

uses SysUtils, Windows, Classes, DECCipher, DECFmt, DecUtil;

function Encrypt(AStr: string): string;
function Decrypt(AStr: string): string;

implementation

const
  GLUE = '::';
  cPASSWORD = 'myownpassword';

function Encrypt(AStr: string): string;
var
  c: TDecCipher;  
  sKey, 
  sIv,  
  sEncrypted,  
  sPlain: AnsiString;  
  iPosGlue,  
  iLength: Integer;  
begin
  
  sKey := cPASSWORD;
  iLength := 16;
  SetLength(sIv, iLength);

  // Expect DEC 5.2 to only deal with AES-128-CBC, not 256.
  c := ValidCipher(DecCipher.TCipher_Rijndael).Create;
  try
    c.Mode := cmCBCx;
    c.Init(sKey, sIv); // Provide binary key and binary IV

    sPlain := AStr;
    iLength := Length(sPlain);
    SetLength(sEncrypted, iLength); // By now the output length must match the input's
    c.Encode(sPlain[1], sEncrypted[1], iLength);

  finally
    c.Free;
  end;

  Result := TFormat_MIME64.Encode(sEncrypted) + GLUE + TFormat_MIME64.Encode(sIv) + GLUE + IntToStr(iLength);
end;
我确信变量Iv的初始化缺少了一些东西,如果有人能指出错误,我将非常高兴

更新:
作为第一步,我已经完成了Encrypt的实现,并让它在Delphi中工作。请参见下面的答案。有了这些,我似乎在代码中发现了一个完全不同的、不相关的bug,我将在另一个问题中发布。

根据上面Michael&Olivier的评论,我对代码进行了更正,并在Delphi中实现了Encrypt,代码如下所示

代码:


根据上面Michael&Olivier的评论,我对代码进行了修改,并在Delphi中实现了Encrypt,代码如下所示

代码:

什么是街区? 例如,在这种情况下,仅使用相同的固定长度16字节=128位。不可能提供与这16个字节不匹配的数据。要加密或解密的数据长度必须始终为16字节=128位。或者是那个长度的倍数

填充在哪里适用? 在现实世界中,数据通常是所需块大小的倍数。密钥、初始化向量和数据都需要匹配块大小。如果没有,则必须将其填充或切割至该尺寸:

我的示例中的密钥大小已经是16个字节。最有可能的情况是,它应该是文本密码的散列,而不是像我的示例中那样伪装成二进制的ASCII,而且-历史上经常选择。OpenSSH会在密钥太短时自动将其填充为0,DEC5.2也是如此-这意味着您可以在这里和PHP中使用较短的密钥,并且输出应该相同。 IV不需要进一步解释:它是所有这一切中最随机的部分,所以立即将其设置为16字节应该没有问题。 数据可以以各种方式填充,OpenSSH默认使用要填充的字节数作为字节值:如果需要6个字节的填充,则追加6666;如果需要2个字节,则追加22。 为什么OpenSSH会以这种方式填充它? 只有最后一个块可能小于所需的块大小。 解密时,您很可能希望切断填充,而不是将其视为输入的一部分。 你看最后一个字节,发现它是15或更低-现在你看另外14个之前的字节,如果它们也都是15,那么很可能只有填充可以被删除。如果最后一个字节是1,那么就不太清楚了:这是输入数据的一部分还是填充?要决定/知道这取决于您,即如果您的输入数据是文本,则可能永远不会出现具有此类值的字节。从这个角度来看,它可能比只填充0字节要好。 如说明中所述,填充;也提到这一点

使用DEC5.2在D7中加密 两端的填充必须相同:要么您指示OpenSSL使用0填充,要么您必须模仿PKCS 7填充,因为DEC不是OpenSSL。当然:您也可以立即在Delphi中使用OpenSSL,而不是依赖DEC,但这样一来,所有细节都不会浮出水面——我更希望他们能够知道哪些部分可能会损坏,而不是将所有的魔法隐藏起来,只调用一个完成所有工作的函数。最后,你迟早要明白加密是如何工作的——如果你从来没有尝试过使用一种工具进行加密,而使用另一种工具进行解密,那你就是如履薄冰。

什么是块? 例如,在这种情况下,仅使用相同的固定长度16字节=128位。不可能提供与这16个字节不匹配的数据。要加密或解密的数据长度必须始终为16字节=128位。或者是那个长度的倍数

填充在哪里适用? 在现实世界中,数据通常是所需块大小的倍数。密钥、初始化向量和数据都需要匹配块大小。如果没有,则必须将其填充或切割至该尺寸:

我的示例中的密钥大小已经是16个字节。最有可能的情况是,它应该是文本密码的散列,而不是像我的示例中那样伪装成二进制的ASCII,而且-历史上经常选择。OpenSSH会在密钥太短时自动将其填充为0,DEC5.2也是如此-这意味着您可以在这里和PHP中使用较短的密钥,并且输出应该相同。 静脉注射无需进一步解释:这是mo st随机部分,所以立即将其设置为16字节应该没有问题。 数据可以以各种方式填充,OpenSSH默认使用要填充的字节数作为字节值:如果需要6个字节的填充,则追加6666;如果需要2个字节,则追加22。 为什么OpenSSH会以这种方式填充它? 只有最后一个块可能小于所需的块大小。 解密时,您很可能希望切断填充,而不是将其视为输入的一部分。 你看最后一个字节,发现它是15或更低-现在你看另外14个之前的字节,如果它们也都是15,那么很可能只有填充可以被删除。如果最后一个字节是1,那么就不太清楚了:这是输入数据的一部分还是填充?要决定/知道这取决于您,即如果您的输入数据是文本,则可能永远不会出现具有此类值的字节。从这个角度来看,它可能比只填充0字节要好。 如说明中所述,填充;也提到这一点

使用DEC5.2在D7中加密
两端的填充必须相同:要么您指示OpenSSL使用0填充,要么您必须模仿PKCS 7填充,因为DEC不是OpenSSL。当然:您也可以立即在Delphi中使用OpenSSL,而不是依赖DEC,但这样一来,所有细节都不会浮出水面——我更希望他们能够知道哪些部分可能会损坏,而不是将所有的魔法隐藏起来,只调用一个完成所有工作的函数。最后,你迟早要明白加密是如何工作的——如果你从来没有尝试使用一种工具加密,另一种工具解密,那你就是如履薄冰。

在你的代码片段中,我看不到你为函数提供了任何随机生成的IV?密钥不应该是一个16字节的数组吗?PHP函数填充了键太短时,使用NUL字节。在Delphi中也应该这样做。您的密钥在这里只有13个字节,您应该添加3个NUL字节;RandomBuffersIv[1],i长度;在这个更改之后,我现在可以在Delphi中正确地解密字符串,这意味着加密函数可以工作,但仍然无法在PHP中解密字符串。这意味着PHP中的解密函数有问题。我不确定@Olivier我添加了sKey:=sKey+StringOfChar0,iLength-LengthsKey;但它仍然不起作用。在您的代码片段中,我看不到您为函数提供了任何随机生成的IV?键不应该是16字节的数组吗?PHP函数在键太短时用NUL字节填充键。在Delphi中也应该这样做。您的密钥在这里只有13个字节,您应该添加3个NUL字节;RandomBuffersIv[1],i长度;在这个更改之后,我现在可以在Delphi中正确地解密字符串,这意味着加密函数可以工作,但仍然无法在PHP中解密字符串。这意味着PHP中的解密函数有问题。我不确定@Olivier我添加了sKey:=sKey+StringOfChar0,iLength-LengthsKey;但它仍然不起作用。除非您的PHP代码看起来与我的示例不同,否则解密就不能起作用,因为OpenSSL不需要0填充。你的问题的重点不是两端都使用它吗?@AmigoJack我还不能用PHP测试解密,因为过程中的中间步骤有一个错误bug。我在这里发布了另一个问题:@AmigoJack我已经添加并测试了您在上面的答案中提供的代码,发现PHP中的解密工作正常。除非您的PHP代码看起来与我的示例不同,否则无法解密,因为OpenSSL不需要0填充。你的问题的重点不是两端都使用它吗?@AmigoJack我还不能用PHP测试解密,因为过程中的中间步骤有一个错误bug。我在这里发布了另一个问题:@AmigoJack我已经添加并测试了您在上面的答案中提供的代码,发现PHP中的解密工作正常。谢谢您的回答。我添加了填充代码iPlus:=16-I长度mod 16;sPlain:=sPlain+StringOfChar Chr iPlus,iPlus;解密现在在PHP中正常工作。谢谢你的回答。我添加了填充代码iPlus:=16-I长度mod 16;sPlain:=sPlain+StringOfChar Chr iPlus,iPlus;现在,解密在PHP中正常工作。
uses SysUtils, Windows, Classes, DECCipher, DECFmt, DecUtil;

function Encrypt(AStr: string): string;

implementation

const
  GLUE = '::';
  cPASSWORD = 'myownpassword';

function Encrypt(AStr: string): string;
var
  c: TDecCipher; // Successfully tested with DEC 5.2 on Delphi 7
  sKey, // The binary key we have to provide
  sIv, // Initialization vector, random bytes
  sEncrypted, // Encrypted binary we want to get
  sPlain: AnsiString; // Actual data to encrypt
  iLength: Integer; // Plaintext length target, in bytes
begin

  sKey := cPASSWORD;
  iLength := 16;
  // ** sKey := sKey + StringOfChar(#0, iLength - Length(sKey));
  SetLength(sIv, iLength);
  RandomBuffer(sIv[1], iLength);

  // Expect DEC 5.2 to only deal with AES-128-CBC, not 256.
  c := ValidCipher(DecCipher.TCipher_Rijndael).Create;
  try
    c.Mode := cmCBCx;
    c.Init(sKey, sIv); // Provide binary key and binary IV

    sPlain := AStr;
    iLength := Length(sPlain);
    SetLength(sEncrypted, iLength); // Set the output byte array length 
    c.Encode(sPlain[1], sEncrypted[1], iLength);

  finally
    c.Free;
  end;

  Result := TFormat_MIME64.Encode(sEncrypted) + GLUE + TFormat_MIME64.Encode(sIv) + GLUE + IntToStr(iLength);
end;

function Decrypt(AStr: string): string;
var
  c: TDecCipher; // Successfully tested with DEC 5.2 on Delphi 7
  sKey, // The binary key we have to provide
  sIv, // Initialization vector, decoded from AStr
  sEncrypted, // Actual data to decrypt, decoded from AStr
  sPlain: AnsiString; // Decrypted binary we want to get
  iPosGlue, // Next found glue token to cut one part off
  iLength: Integer; // Plaintext length target, in bytes
begin
  iPosGlue := Pos(GLUE, AStr);
  sEncrypted := Copy(AStr, 1, iPosGlue - 1); // Still Base64
  Delete(AStr, 1, iPosGlue - 1 + Length(GLUE));

  iPosGlue := Pos(GLUE, AStr);
  sIv := Copy(AStr, 1, iPosGlue - 1);
  Delete(AStr, 1, iPosGlue - 1 + Length(GLUE));

  iLength := StrToInt(AStr);

  sKey := cPASSWORD;

  // Decode Base64 back into binary
  sEncrypted := TFormat_MIME64.Decode(sEncrypted);
  sIv := TFormat_MIME64.Decode(sIv);

  // Expect DEC 5.2 to only deal with AES-128-CBC, not 256.
  c := ValidCipher(DecCipher.TCipher_Rijndael).Create;
  try
    c.Mode := cmCBCx;
    c.Init(sKey, sIv); // Provide binary key and binary IV
    SetLength(sPlain, Length(sEncrypted)); // By now the output length must match the input's
    c.Decode(sEncrypted[1], sPlain[1], Length(sEncrypted));
    SetLength(sPlain, iLength); // Now cut it to the actual expected length
  finally
    c.Free;
  end;

  Result := sPlain;
end;

// Sample implementation: (Delphi 7)
// var
//   s: string;
// begin
//   s := 'The quick brown fox jumps over the lazy rabbit..';
//   WriteLn(s);
//   s := Encrypt(s);
//   WriteLn(s);
//   s := Decrypt(s);
//   WriteLn(s);
// 
// end;
const  // The same glue for concatenating all 3 parts
  GLUE= '::';
var
  c: TDecCipher;  // Successfully tested with DEC 5.2 on Delphi 7
  sKey,  // The binary key we have to provide
  sIv,  // Initialization vector, should be random in the real world
  sEncrypted,  // Output of the encryption
  sPlain: AnsiString;  // What we want to encrypt, in binary
  iPlus,  // Input data padding
  iLength: Integer;  // Plaintext length source, in bytes
begin
  // Keep in mind: Plain, Key and IV are all binary, not text!
  sPlain:= 'AbCdEfGhIjKlMnOpQrStUvWxYz';
  sKey:= '1234567890123456';
  sIv:= #$9e#$8e#$5d#$5a#$b9#$09#$d9#$3c#$99#$1f#$d6#$04#$b9#$8f#$4f#$50;
  iLength:= Length( sPlain );

  // The cipher/algorithm depends on fixed block sizes, so it is automatically
  // padded to the next full length. OpenSSL's padding byte is equal to the amount
  // of bytes to be added: if 6 bytes need to be added, then 6 times #6 is added.
  iPlus:= 16- (iLength mod 16);
  sPlain:= sPlain+ StringOfChar( Chr( iPlus ), iPlus );

  // Expect DEC 5.2 to only deal with AES-128-CBC, not 256.
  c:= ValidCipher( DecCipher.TCipher_Rijndael ).Create;
  try
    c.Mode:= cmCBCx;
    c.Init( sKey, sIv );  // Provide binary key and binary IV
    SetLength( sEncrypted, Length( sPlain ) );  // Both are multiples of 16
    c.Encode( sPlain[1], sEncrypted[1], Length( sPlain ) );

    // Glue it together, should be...
    Writeln
    ( TFormat_MIME64.Encode( sEncrypted )  // '9NC0HhAxFZLuF/omOcidfDQnczlczTS1nIZkNPOlQZk='
    + GLUE+ TFormat_MIME64.Encode( sIv )   // '::no5dWrkJ2TyZH9YEuY9PUA=='
    + GLUE+ IntToStr( iLength )            // '::26'
    );
  finally
    c.Free;
  end;
end;