Delphi 7 - 用 DEC 加密,用 PHP OpenSSL 解密

如何解决Delphi 7 - 用 DEC 加密,用 PHP OpenSSL 解密

使用:Delphi 7,DEC v5.2

请参考这个问题:Delphi 7 - DCPCrypt - TDCP_rijndael - DecryptString - How to make it work?

从@AmigoJack 的出色回答中,我的 Delphi Summary of Sales 函数运行良好。基于此,我现在正在尝试实现 Decrypt 函数,但到目前为止还没有成功。发生的情况是加密是在 Delphi 中完成的,而在 PHP 中解密的字符串产生的字符串与加密的字符串不同,这意味着 Delphi 代码中存在问题。

这是代码

Encrypt

我确信变量 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; 的初始化缺少一些东西,如果有人能指出错误,我真的很高兴。

更新: 作为第一步,我已经完成了 Encrypt 的实现并让它在 Delphi 中工作(见下面我的回答)。有了这个,我似乎在代码中发现了一个完全不同的、不相关的错误,我将在一个单独的问题中发布。

解决方法

什么是块?

A block cipher(例如 AES/Rijndael)仅适用于相同的固定长度(在本例中为 16 字节 = 128 位)。提供与这 16 个字节不匹配的数据是不可能的。您要加密或解密的数据长度必须始终为 16 字节 = 128 位。或该长度的倍数。

填充在哪里应用?

在现实世界中,您的数据很少是所需块大小的倍数。 初始化向量数据都需要匹配块大小。如果不是,则必须将它们填充(或切割)到该尺寸:

  • 我示例中的 key 大小已经是 16 个字节。很可能它应该是文本密码的散列(而不是像我的示例中那样伪装成二进制的 ASCII),并且 there are enough available that output 128 bit = 16 byte - 历史上经常选择 MD5OpenSSH 会在密钥太短时自动用 #0 填充,DEC5.2 也是如此 - 这意味着您可以在此处和 PHP 和输出应该是一样的。
  • IV 无需进一步解释:它是所有这些中最随机的部分,因此立即将其设为 16 个字节应该没有问题。
  • 数据可以通过多种方式填充,OpenSSH默认使用要填充的字节数作为字节值:如果需要填充 6 个字节,则附加了 #6#6#6#6#6#6;如果需要 2 个字节,则附加 #2#2

为什么 OpenSSH 以这种方式填充?

  • 只有所有块中的最后一个可能小于所需的块大小。
  • 解密时,您很可能希望切断该填充,而不是将其视为输入的一部分。
  • 您查看最后一个字节并意识到它是 #15 或更低 - 现在您查看其他 14 个前面的字节,如果它们也都是 #15,那么很可能只有可以剪切的填充离开。如果最后一个字节是 #1 则不太清楚:这是输入数据的一部分还是填充?决定/知道这取决于您(即,如果您的输入数据是文本,则可能永远不会出现具有此类值的字节)。从您的角度来看,它可能比仅填充 #0 字节要好。
PKCS #7 中指定的

RFC 2315 §10.3 解释了填充; a comment in PHP's manual to openssl_encrypt() 也提到了这一点。

使用 DEC5.2 在 D7 中加密

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;

两端的填充必须相同:要么指示 OpenSSL 使用 #0 填充,要么必须模仿 PKCS #7填充在您身边(因为 DEC 不是 OpenSSL)。当然:您也可以立即在 Delphi 中使用 OpenSSL 而不是依赖于 DEC,但随后不会出现任何细节 - 我而是想知道他们能够知道哪些部分可能会损坏,而不是将所有“魔法”隐藏在幕后,只调用一个函数来完成所有工作。最终,人们迟早会了解加密是如何工作的——如果您从未尝试使用一种工具进行加密,而使用另一种工具进行解密,那么您就如履薄冰。

,

根据上述 Michael 和 Olivier 的评论,我对代码进行了更正,并在 Delphi 中实现了 Encrypt 的工作实现,代码如下。

代码:

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,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],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,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,iPosGlue - 1 + Length(GLUE));

  iPosGlue := Pos(GLUE,AStr);
  sIv := Copy(AStr,iPosGlue - 1);
  Delete(AStr,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,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;

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?