Delphi 7 - DCPCrypt - TDCP_rijndael - DecryptString - 如何让它工作?

如何解决Delphi 7 - DCPCrypt - TDCP_rijndael - DecryptString - 如何让它工作?

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

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

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

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,$iv);
    return base64_encode($encrypted . '::' . $iv);
}

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

德尔福代码

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!

解决方法

需要了解的基础知识

  • 永远不要处理“文本”,因为加密不知道 text encodings
  • 永远不要处理“Strings”,因为它们在不同的编程语言之间差异很大。
  • 不同的密码具有不同的块大小 - 这意味着提供的要加密的数据必须匹配可以被给定除数(即 8 或 16)整除的长度。否则会应用填充,您可能需要注意这一点。
  • OpenSSL 的主要目标受众是 MIME/电子邮件,因此它已经在 Base64 上运行。不要再次将其输出重新编码为 Base64 - 这只是错过了要点。
  • 密钥始终是二进制的。将其作为原始字节处理。 ASCII 也有效,这只是巧合。但是,一旦超出此范围,请重新考虑您将要做什么。

为什么要处理 Base64?

这只是一种以坚如磐石的格式存储二进制数据的方法。它的尺寸更大,但即使通过电子邮件发送也是安全的。如果您没有这种需求,因为无论如何您都会将数据存储到文件中,那么(当然)不要使用它。

使用 OpenSSL 在 PHP 中加密和解密

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

<?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,$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,$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;

如果选择了 9e8e5d5ab909d93c991fd604b98f4f50initialization vector(其 16 字节长度的 hexadecimal 表示),那么加密应该产生一个 9NC0HhAxFZLuF/omOcidfDQnczlczTS1nIZkNPOlQZk=::no5dWrkJ2TyZH9YEuY9PUA==::26 的一体式文本,其中第一部分是 Base64 中的加密数据,第二部分是 Base64 中的初始化向量,第三部分确保我们的纯(文本)输入的长度。使用该长文本,您应该能够将其解码回 AbCdEfGhIjKlMnOpQrStUvWxYz 的纯文本(文本)(长度为 26 字节)。

使用 DEC5.2 在 D7 中解密

我不完全确定,但 Delphi Encryption Compendium 5.2,Part I 似乎不支持 AES 的不同密钥大小,这就是我坚持使用 128 的原因。请记住,Delphi 7 的 String 必须始终被视为AnsiString 在其他版本中,否则你最终会得到一些不是字节安全的东西。

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

  iPosGlue:= Pos( GLUE,sAllInOne );
  sIv:= Copy( sAllInOne,iPosGlue- 1 );
  Delete( sAllInOne,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 字节的大小。

剩下的呢?

应该是显而易见的。 Delphi 中的加密非常相似。使用 ASCII 以外的文本作为有效负载和/或密钥是完全可能的,但很可能不会在幕后神奇地完成 - 确保您实际上有 ie UTF-8 或 {{3}通过遍历所有代码行并跟踪内存是否真的保存了您期望的字节,无处不在。如果你不喜欢文本编码,那么把它留给其他人。如果您不喜欢加密,那么将处理文本的工作留给其他人。

如果您介意所有步骤,在 Delphi 中使用不同的库/组件(即支持 AES-256 的库/组件)应该很容易与我的示例互换。如果您从互联网上获取了一个狂野的 Base64 编码器/解码器,请注意也有略微不同的版本。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 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元字符(。)和普通点?