如何解决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 - 历史上经常选择 MD5。 OpenSSH 会在密钥太短时自动用
#0
填充,DEC5.2 也是如此 - 这意味着您可以在此处和 PHP 和输出应该是一样的。 - IV 无需进一步解释:它是所有这些中最随机的部分,因此立即将其设为 16 个字节应该没有问题。
-
数据可以通过多种方式填充,OpenSSH默认使用要填充的字节数作为字节值:如果需要填充 6 个字节,则附加了
#6#6#6#6#6#6
;如果需要 2 个字节,则附加#2#2
。
为什么 OpenSSH 以这种方式填充?
- 只有所有块中的最后一个可能小于所需的块大小。
- 解密时,您很可能希望切断该填充,而不是将其视为输入的一部分。
- 您查看最后一个字节并意识到它是
#15
或更低 - 现在您查看其他 14 个前面的字节,如果它们也都是#15
,那么很可能只有可以剪切的填充离开。如果最后一个字节是#1
则不太清楚:这是输入数据的一部分还是填充?决定/知道这取决于您(即,如果您的输入数据是文本,则可能永远不会出现具有此类值的字节)。从您的角度来看,它可能比仅填充#0
字节要好。
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 举报,一经查实,本站将立刻删除。