我有两种 AES-256-CBC 加密实现,一种在 PHP 中,另一种在 Python 中。尽管试图使两个版本尽可能相似,但加密的输出是不同的。我想了解可能导致这种差异的原因以及如何确保两者给出相同的结果。
这是我的 PHP 代码:
function encryptCardInfo()
{
$string = preg_replace("/[^A-Za-z0-9 ]/", '', $this->secret_key);
$encryption_key = substr($string, 0, 16);
$encryption_key = substr(hash('sha256', $this->secret_key, true), 0, 16);
// Generate an initialization vector (IV)
$iv = str_repeat("\x00", openssl_cipher_iv_length('aes-256-cbc'));
// Encrypt the data
$imploded_string = $this->array_implode_with_keys($this->array_params);
$encrypted = openssl_encrypt($imploded_string, 'aes-256-cbc', $encryption_key, 0, $iv);
$final_result = base64_encode($encrypted . '::' . $iv);
echo "PHP Final Result: " . $final_result . "\n";
return $final_result;
}
function array_implode_with_keys($array) {
$return = '';
if (count($array) > 0) {
foreach ($array as $key => $value) {
$return .= $key . '||' . $value . '__';
}
$return = substr($return, 0, strlen($return) - 2);
}
return $return;
}
这是我的Python代码:
import re
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
def encrypt_card_info(self, array_params):
# Clean the secret key and generate a 16-character encryption key
string = re.sub(r'[^A-Za-z0-9]', '', self.secret_key)
encryption_key = string[:16].encode('utf-8')
# Generate an initialization vector (IV)
iv = bytes([0] * AES.block_size)
# Create imploded string (card information)
string_data = self.array_implode_with_keys(array_params)
# Padding using PKCS7
padded_data = pad(string_data.encode('utf-8'), AES.block_size)
# Encrypt the data using AES 256 encryption in CBC mode
cipher = AES.new(encryption_key, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(padded_data)
# Combine the encrypted data with the IV and encode in base64
final_result = base64.b64encode(encrypted + iv).decode('utf-8')
return final_result
def array_implode_with_keys(self, data):
return '__'.join(f"{key}||{value}" for key, value in data.items())
在这两种情况下,密钥都会被清理、截断并用于生成 16 个字符的加密密钥。我使用带有归零 IV 的 AES-256-CBC 模式,生成的加密数据与 IV 组合并以 base64 编码。
我检查了以下内容:
两者都使用 AES-256-CBC。 应用相同的填充方法(PKCS7)。 两者都使用相同的密钥和 IV(一串零)。
什么可能导致 PHP 和 Python 之间的加密输出存在这些差异,以及如何修改 Python 实现以匹配 PHP 的结果?
hash
函数返回字节的十六进制表示形式,因此用 substr 删除其中的 16 个字符会导致 IV 为“20139ebeee312271”(与 key 的结果类似)。这些不是真正的字节,它们是 [0-9a-f]
范围内的字符。这不是你想要做的。
Python
sha256
.digest()
函数返回字节字符串,而不是十六进制表示形式。删除其中的 16 个字符会从哈希中得到 16 个真实字节。这可能就是您打算做的。
结果不同,因为您使用不同的密钥和初始化向量进行加密。
从文档来看,PHP
hash
可以采用第三个参数 binary
,默认为 false:
hash( string $algo, string $data, bool $binary = false, array $options = [] ): string
设置为 true 时,输出原始二进制数据。 false 输出小写十六进制。binary
将 PHP 代码更改为:
<?php
$plaintextstr = 'AAAAAAAAAAAAAAAA';
$encrypt_method = "AES-256-CBC";
$secret_key = "SSSSSSSSSSSS";
$secret_iv = "LLLLLLLLLLLL";
$key = substr(hash('sha256', $secret_key, true), 0, 32);
$iv = substr(hash('sha256', $secret_iv, true), 0, 16);
$encrypted_str = openssl_encrypt($plaintextstr, $encrypt_method, $key, 0, $iv);
echo $encrypted_str;
// Result: V7zSp9KHPke9QuPbWoUNvjCHVJ7giluD9YaOnX9E57k=
产生与 python 代码相同的结果。