Delphi 7-使用DEC加密,然后使用PHP OpenSSL解密 [英] Delphi 7 - Encrypt with DEC, and decrypt with PHP OpenSSL

查看:86
本文介绍了Delphi 7-使用DEC加密,然后使用PHP OpenSSL解密的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用:Delphi 7,DEC v5.2

Using: Delphi 7, DEC v5.2

请参考以下问题:从@AmigoJack的出色答案中,我可以很好地使用Delphi Decrypt 函数.基于此,我现在正在尝试实现 Encrypt 函数,但到目前为止尚未成功.发生的事情是加密是在Delphi中完成的,而在PHP中解密时,该字符串产生的字符串与加密后的字符串不同,这表明Delphi代码中有问题.

From @AmigoJack's excellent answer, I have the Delphi Decrypt function working fine. Based on that, I am now trying to implement the Encrypt function but have been unsuccessful so far. What is happening is that the encryption is done in Delp and the string when decrypted in PHP is producing a different string than what was encrypted, implying that something is wrong in the Delphi code.

这是代码:

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 的初始化中缺少某些内容,如果有人指出错误,我将非常高兴.

I am sure that there is something missing with initialization of the variable Iv, and would really be glad if someone could point out the mistake.

更新:第一步,我已经完成Encrypt的实现,并使其在Delphi中工作(请参见下面的答案).这样,我似乎在代码中发现了一个完全不同的,不相关的错误,我将在另一个问题中发帖.

UPDATE: As a first step, I've completed the implementation for Encrypt and have it working in Delphi (see my answer below). With that, I seem to have found a completely different, unrelated bug in the code, that I will post in a separate question.

推荐答案

什么是块?

分组密码(例如

What's a block?

A block cipher (such as AES/Rijndael) only works with the same fixed length (in this case 16 byte = 128 bit). Providing data that doesn't match these 16 bytes is not possible. The data you want to encrypt or decrypt must always be 16 byte = 128 bit in length. Or multiples of that length.

在现实世界中,您的数据通常是所需块大小的倍数.初始化向量数据都需要匹配块大小.如果不是,则必须将其填充(或剪切)为该大小:

In the real world your data rarily is a multiple of the needed block size. The key, the initialization vector and the data all need to match the block size. If not, they must be padded (or cut) to that size:

  • The key sizes in my example already 16 bytes. Most likely it should be a hash of a textual password (instead of ASCII disguised as binary, as in my example), and there are enough available that output 128 bit = 16 byte - historically MD5 was chosen often. OpenSSH will automatically pad a key with #0 when it's too short and so does DEC5.2 - that means you can use a shorter key both here and in PHP and the output should be the same.
  • The IV needs no further explanation: it's the most random part in all this, so there should be no problem in making it 16 bytes right away.
  • The data can be padded in various ways and OpenSSH by default uses the amount of bytes to be padded as byte value: if 6 bytes of padding are needed, then #6#6#6#6#6#6 is appended; if 2 bytes are needed, then #2#2 is appended.
  • 仅所有块中的最后一个可能小于所需的块大小.
  • 解密时,您最有可能希望切断该填充,而不是将其视为输入的一部分.
  • 您查看最后一个字节并意识到它是#15 或更低的字节-现在您查看其他14个前面的字节,如果它们也都是#15 ,则为最有可能的是只能剪掉的填充物.如果最后一个字节是#1 ,那么它不是很清楚:这是输入数据的一部分还是填充?决定/知道由您自己决定(即,如果您输入的数据是文本,则可能永远不会出现具有此类值的字节).从您的角度来看,这可能比仅填充#0 字节更好.
  • Only the last of all blocks might be shorter than the needed block size.
  • When decrypting you most likely want to cut off that padding instead of seeing it as part of your input.
  • You look at the last byte and realize it's #15 or lower - now you look at the 14 other preceeding bytes and if they also all are #15 then it's most likely only padding that can be cut off. If the last byte is a #1 then it's not so clear: is this part of the input data or is it padding? To decide/know that it is up to you (i.e. if your input data was text then bytes with such values might never occur). Given the perspective you look at it it might be better than only padding #0 bytes.

PKCS#7 .ietf.org/html/rfc2315#page-22"rel =" nofollow noreferrer> RFC 2315§10.3解释了该填充; PHP手册中对 openssl_encrypt()的注释 也提到了这一点.

PKCS #7 as specified in RFC 2315 §10.3 explains the padding; a comment in PHP's manual to openssl_encrypt() also mentions this.

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 ,但是随后没有任何细节浮出水面-我而是想知道他们能够知道哪些部分可能破裂而不是保留所有的魔术"元素.在后台仅调用一个函数即可完成所有工作.最后,您迟早必须了解加密的工作原理-如果您从未尝试使用一种工具进行加密而另一种工具进行解密,那么您将步履蹒跚.

The padding needs to be the same on both ends: either you instruct OpenSSL to use #0 padding, or you have to mimic the PKCS #7 padding on your side (because DEC is not OpenSSL). Of course: you could also use OpenSSL in Delphi right away instead of relying on DEC, but then none of the details would have surfaced - I rather want to know them to be able to know which parts may break instead of keeping all the "magic" under the hood and only calling one function which does all the work. In the end one has to understand sooner or later how cryption works - you're walking on thin ice if you never tried to use one tool to encrypt and a different one to decrypt.

这篇关于Delphi 7-使用DEC加密,然后使用PHP OpenSSL解密的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆