Ste*_*e F 4 delphi encryption delphi-7 encryption-symmetric php-openssl
我正在 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, 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);
}
Run Code Online (Sandbox Code Playgroud)
德尔福代码:
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;
Run Code Online (Sandbox Code Playgroud)
..项目截止日期很紧,并且我一直在这个问题上..非常感谢您为解决问题提供帮助。蒂亚!
这只是一种以坚如磐石的格式存储二进制数据的方法。它的尺寸更大,但甚至可以安全地通过电子邮件发送。如果您没有这种需要,因为无论如何您都会将数据存储到文件中,那么(当然)不要使用它。
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, 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 emails.
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, so
// we chose "::" for it. If everything works as expected we get an
// array with exactly 3 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;
Run Code Online (Sandbox Code Playgroud)
如果选择初始化向量(9e8e5d5ab909d93c991fd604b98f4f50
其16 字节长度的十六进制表示),则加密应生成一个完整的文本,其中9NC0HhAxFZLuF/omOcidfDQnczlczTS1nIZkNPOlQZk=::no5dWrkJ2TyZH9YEuY9PUA==::26
第一部分是 Base64 中的加密数据,第二部分是 Base64 中的初始化向量,第三部分确保纯文本输入的长度。使用该长文本,您应该能够将其解码回纯文本AbCdEfGhIjKlMnOpQrStUvWxYz
(长度为 26 字节)。
我不完全确定,但Delphi Encryption Compendium 5.2,第 I 部分似乎不支持 AES 的不同密钥大小,这就是我坚持使用 128 的原因。请记住,Delphi 7String
必须始终像其他版本一样对待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, 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! 16 full bytes.
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;
Run Code Online (Sandbox Code Playgroud)
由于不使用 OpenSSL,我们需要自己处理块大小填充 - 如果省略最后一个长度分配,您将看到有更多字节可以四舍五入到 32 字节的大小。
应该是显而易见的。Delphi 中的加密非常相似。使用 ASCII 之外的文本作为有效负载和/或密钥是完全可能的,但很可能不会在幕后神奇地完成 -通过单步执行所有代码行和确保您实际上到处都有UTF-8或ISO-8859-1跟踪内存是否确实保存了您期望的字节。如果您不喜欢文本编码,那就留给其他人吧。如果您不喜欢加密,那么将文本处理留给其他人。
如果您介意所有步骤,在 Delphi 中使用不同的库/组件(即支持 AES-256 的库/组件)应该可以轻松地与我的示例进行交换。如果您从互联网上获取一个野生的 Base64 编码/解码器,那么请注意,也存在略有不同的版本。
归档时间: |
|
查看次数: |
2994 次 |
最近记录: |