Delphi 7-DCPCrypt-TDCP_rijndael-DecryptString-如何使其工作? [英] Delphi 7 - DCPCrypt - TDCP_rijndael - DecryptString - How to make it work?

查看:91
本文介绍了Delphi 7-DCPCrypt-TDCP_rijndael-DecryptString-如何使其工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用PHP(openssl_encrypt/'aes-256-cbc')加密文本,然后尝试在Delphi 7(DCPCrypt/TDCP_rijndael)中对其进行解密.

I'm encrypting text in PHP (openssl_encrypt / 'aes-256-cbc') and then trying to decrypt it in Delphi 7 (DCPCrypt / TDCP_rijndael).

PHP脚本文件以ANSI编码保存,希望传输的字符串(其REST API Web服务)与Delphi兼容.

The PHP script file is saved with ANSI encoding, in the hope that the string transmitted (its a REST API web service) is compatible with Delphi.

但是,Delphi解密产生了错误的结果,我猜测代码中有问题.如果您能看一眼,并在Delphi方面发现我的错误,我将不胜感激:

However the Delphi decryption is producing the wrong result, I am guessing that something is wrong in the code. I would be grateful if you could have a look, and spot my error on the Delphi side:

PHP代码:

function encrypt($key, $payload) {
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
    $encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv);
    return base64_encode($encrypted . '::' . $iv);
}

function decrypt($key, $garble) {
        list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2);
        return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
}

Delphi代码:

var
  DCP_rijndael: TDCP_rijndael;

const
  cPASSWORD = 'myownpassword';

function Decrypt(AStr: string): string;
var
  d, s, iv: String;
  p: Integer;
begin
  d := Base64DecodeStr(AStr);
  p := Pos('::', d);
  s := Copy(d, 1, p - 1);
  iv := Copy(d, p + 2, Length(s));

  DCP_rijndael.SetIV(iv);
  Result := DCP_rijndael.DecryptString(s);
end;

initialization
  DCP_rijndael := TDCP_rijndael.Create(nil);
  DCP_rijndael.Algorithm := 'Rijndael';
  DCP_rijndael.CipherMode := cmCBC;

  //DCP_rijndael.BlockSize := 128; {tried various values with no luck!}
  //DCP_rijndael.MaxKeySize := 256;{tried various values with no luck!}

  DCP_rijndael.Init(cPASSWORD, 256, nil);

finalization
  DCP_rijndael.Free;

..的项目截止日期很紧,并坚持下去..非常感谢您为解决该问题提供帮助.TIA!

..have a tight project deadline, and am stuck on this ..would really appreciate assistance in resolving the issue. TIA!

推荐答案

需要理解的基础

  • 从不处理文本",因为加密不知道文本编码.
  • 从不处理"字符串",因为它们之间的差异很大编程语言.
  • 不同的密码具有不同的块大小-这意味着所提供的要加密的数据必须匹配可以由给定的除数(即8或16)除以的长度.否则,将使用填充,您可能需要注意这一点.
  • OpenSSL的主要目标受众是MIME/电子邮件,因此它已经在 Base64 上运行.不要再次将其输出重新编码到Base64中-只是遗漏了这一点.
  • 密钥始终是二进制的.将其作为原始字节处理. ASCII 也是有效的,这只是巧合.但是,一旦超出此范围,就重新考虑要做什么.
  • Basics that need to be understood

    • Never deal with "text", as encryption is not aware of text encodings.
    • Never deal with "Strings", as those differ drastically between programming languages.
    • Different ciphers have different block sizes - this implies that the provided data to encrypt must match a length that can be divided by a given divisor (i.e. 8 or 16). Otherwise padding applies, and you might need to take care of that.
    • OpenSSL's primary target audience is MIME/e-mail, hence it already operates on Base64. Do not re-encode its output into Base64 again - that's just missing the point.
    • A key is always binary. Deal with it as raw bytes. It's only coincidence that ASCII works, too. But as soon as you're beyond that, reconsider what you're about to do.
    • 这只是一种以坚如磐石的格式存储二进制数据的方法.它的大小更大,但是可以安全地通过电子邮件发送.如果您没有此需要,因为无论如何您都将数据存储到文件中,那么(当然)不要使用它.

      It's just a way to store binary data in a rock solid format. It's bigger in size, but even safe to be sent in e-mails. If you don't have that need, because you'd store your data into files anyway, then (of course) don't use it.

      PHP 文件的文本编码与编码和解码无关:这些函数的参数仍被视为二进制.

      The PHP file's text encoding is irrelevant in terms of encoding and decoding: the parameters for those functions are still treated as binary.

      <?php
      
      // This file's output should not be interpreted as HTML
      header( 'Content-type: text/plain' );
      
      // Do not use the same literals again and again
      define( 'CIPHER', 'aes-128-cbc' );  // Which algorithm is used
      define( 'GLUE', '::' );  // How to concatenate data and IV
      
      
      function encrypt( $key, $plain ) {
          // Initialization vector comes in binary. If we want to carry that
          // thru text-like worlds then we should convert it to Base64 later.
          $iv= openssl_random_pseudo_bytes( openssl_cipher_iv_length( CIPHER ) );
          echo "\n iv=\t\t(binary as hex)\t". bin2hex( $iv ). "\tlength=". strlen( $iv );
      
        // By default OpenSSL already returns Base64, but it could be changed 
        // to binary with the 4th parameter, if we want.
          $encryptedData= openssl_encrypt( $plain, CIPHER, $key, 0, $iv );
          echo "\n encrypted=\t(Base64)\t". $encryptedData;
      
          // The encrypted data already came in Base64 - no need to encode it
          // again in Base64. Just concatenate it with the initialization
          // vector, which is the only part that should also be encoded to
          // Base64. And now we have a 7bit-safe ASCII text, which could be
          // easily inserted into e-mails.
          return $encryptedData. GLUE. base64_encode( $iv ). GLUE. strlen( $plain );
      }
      
      function decrypt( $key, $allinone ) {
          // The "glue" must be a sequence that would never occur in Base64.
          // If everything works as expected we get an array with exactly three
          // elements: first is data, second is IV, third is size.
          $aParts= explode( GLUE, $allinone, 3 );
      
          // OpenSSL expects Base64 by default as input - don't decode it!
          $data= $aParts[0];
          echo "\n data=\t\t(Base64)\t". $data;
      
          // The initialization vector was encoded in Base64 by us earlier and
          // now needs to be decoded to its binary form. Should size 16 bytes.
        $iv= base64_decode( $aParts[1] );
          echo "\n iv=\t\t(binary as hex)\t". bin2hex( $iv ). "\tlength=". strlen( $iv );
      
        return openssl_decrypt( $data, CIPHER, $key, 0, $iv );
      }
      
      // Keep in mind that you DON'T encrypt and decrypt "TEXT" - you
      // operate on binary data. Likewise make sure you fully understood
      // this by choosing only ASCII before advancing into the world of
      // different text encodings. Never mix encryption with "Strings" -
      // only operate on it as if it would be naked bytes that make no sense!
      $plain= 'AbCdEfGhIjKlMnOpQrStUvWxYz';
      $key= '1234567890123456';
      
      echo "Parameters:
       plain=\t\t(binary)\t$plain\tlength=". strlen( $plain ). "
       key=\t\t(binary)\t$key\tlength=". strlen( $key ). "
      ";
      
      echo "\nEncryption:";
      $en= encrypt( $key, $plain );
      echo "\n allinone=\t(ASCII)\t\t". $en. "\n";
      
      echo "\nDecryption:";
      $de= decrypt( $key, $en );
      echo "\n decrypted=\t(binary)\t". $de;
      

      如果 9e8e5d5ab909d93c991fd604b98f4f50 初始化矢量( 16字节长度的十六进制表示),然后加密应产生多合一 9NC0HhAxFZLuF/omOcidfDQnczlczTS1nIZkNPOlQZk = :: no5dWrkJ2TyZH9YEuY9PUA == :: 26 的文本,其中第一部分是Base64中的加密数据,第二部分是Base64中的初始化向量,第三部分是Base64的长度我们的纯文本输入.使用该长文本,您应该能够将其解码回 AbCdEfGhIjKlMnOpQrStUvWxYz 的纯文本(长度为26字节).

      If an initialization vector of 9e8e5d5ab909d93c991fd604b98f4f50 (hexadecimal representation of its 16 byte length) is chosen then the encryption should produce an all-in-one text of 9NC0HhAxFZLuF/omOcidfDQnczlczTS1nIZkNPOlQZk=::no5dWrkJ2TyZH9YEuY9PUA==::26 where the first part is the encrypted data in Base64, the second part is the initialiation vector in Base64, and the third part ensures the length of our plain(text) input. Use that long text and you should be able to decode it back to the plain(text) of AbCdEfGhIjKlMnOpQrStUvWxYz (length 26 in bytes).

      我不确定,但是 Delphi加密纲领5.2,第I部分似乎不支持AES的不同密钥大小,这就是为什么我坚持使用128.请记住,在其他版本中,Delphi 7的 String 必须始终视为 AnsiString ,否则您将得到不字节安全的内容.

      I'm not entirely sure but Delphi Encryption Compendium 5.2, Part I doesn't seem to support different key sizes for AES, that's why I stick to 128. Keep in mind that Delphi 7's String must always be treated as AnsiString in other versions, as otherwise you end up with something that isn't byte-safe.

      uses
        DecCipher, DecFmt;
      
      const  // The same glue for concatenating all 3 parts
        GLUE= '::';
      
      var
        c: TDecCipher;  // Successfully tested with DEC 5.2 on Delphi 7
        sAllInOne,  // All 3 parts in a 7bit-safe ASCII text
        sKey,  // The binary key we have to provide
        sIv,  // Initialization vector, decoded from sAllInOne
        sEncrypted,  // Actual data to decrypt, decoded from sAllInOne
        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
        // What was output by the PHP script
        sAllInOne:= '9NC0HhAxFZLuF/omOcidfDQnczlczTS1nIZkNPOlQZk=::no5dWrkJ2TyZH9YEuY9PUA==::26';
      
        // Find next delimiter; Base64 will never have a '..' sequence
        iPosGlue:= Pos( GLUE, sAllInOne );
        sEncrypted:= Copy( sAllInOne, 1, iPosGlue- 1 );  // Still Base64
        Delete( sAllInOne, 1, iPosGlue- 1+ Length( GLUE ) );
      
        iPosGlue:= Pos( GLUE, sAllInOne );
        sIv:= Copy( sAllInOne, 1, iPosGlue- 1 );
        Delete( sAllInOne, 1, iPosGlue- 1+ Length( GLUE ) );
      
        // What remains is the length of the original text, once decrypted. Why do we need it?
        // Because the cipher/algorithm depends on fixed block sizes, so it is automatically
        // padded to the next full length. Otherwise we end up with decryptions that will
        // always have a few odd bytes at the end, if they aren't multiples of 16.
        iLength:= StrToInt( sAllInOne );
      
        // Keep in mind: this is treated as binary, not text!
        sKey:= '1234567890123456';
      
        // 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
      
          // We're done: sPlain should be 'AbCdEfGhIjKlMnOpQrStUvWxYz'
          Writeln( sPlain );
        finally
          c.Free;
        end;
      end;
      

      由于未使用OpenSSL,我们需要自己处理填充的块大小-如果省略最后的长度分配,则会看到有更多字节可以舍入为32字节.

      Since OpenSSL is not used we need to treat the block size padding ourselves - if you omit the last length assignment you'll see there are more byte to round up to a size of 32 bytes.

      应该很明显.Delphi中的加密非常相似.完全有可能使用ASCII以外的文本作为有效载荷和/或密钥,但很可能不会在幕后神奇地完成-确保您实际上 ISO-8859-1 遍及所有代码行遍历所有位置,并跟踪内存是否确实包含您期望的字节.如果您不喜欢文本编码,则将其留给其他人使用.如果您不喜欢加密,那么就让别人来处理文本.

      Should be obvious. Encryption in Delphi is very similar. Using texts beyond ASCII both as payload and/or keys is entirely possible, but will most likely not be magically done behind the scenes - make sure you actually have i.e. UTF-8 or ISO-8859-1 everywhere by stepping thru all code lines and trace if the memory really holds the bytes you expect. If you're not into text encodings, then leave it to others. If you're not into encryption, then leave dealing with text to others.

      在Delphi中使用不同的库/组件(即支持AES-256的库/组件)应该可以很容易地与我的示例互换,只要您注意所有步骤.如果您从互联网上获取疯狂的Base64编码器/解码器,那么请注意,还有一些稍有不同的版本.

      Using a different library/component (i.e. one that supports AES-256) in Delphi should be easily exchangeable with my example if you mind all the steps. If you grab a wild Base64 en-/decoder from the internet then be aware that there are also mildly different versions.

      这篇关于Delphi 7-DCPCrypt-TDCP_rijndael-DecryptString-如何使其工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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