将Python AES ECB移植到PHP OpenSSL

0 投票
1 回答
67 浏览
提问于 2025-04-14 17:17

我有一段Python代码

from Crypto.Cipher import AES


def pad(data):
    block_size = 16
    bytes_to_add = block_size - ((len(data) % block_size) or block_size)
    return data + (b'\0' * bytes_to_add)


cipher = AES.new(b"4452038393672345", AES.MODE_ECB)
body = pad("asa masa".encode('utf-8'))
content = base64.b64encode(cipher.encrypt(body)).decode('ascii')

我看到的结果是 "sEP5RCWmdQdPYo/eeWVIwg=="

我想把这段Python代码转换成PHP,使用openssl

function pad($data) {
  $block_size = 16;
  $bytes_to_add = $block_size - ((strlen($data) % $block_size) ?: $block_size);
  return $data . str_repeat("\0", $bytes_to_add);
}


$cipher = "AES-128-ECB";
$options = OPENSSL_RAW_DATA;
$plainText = pad("asa masa");
$key = '4452038393672345';
$encryptedText = openssl_encrypt($plainText, $cipher, $key, $options);
$encodedText = base64_encode($encryptedText);

我从openssl得到的结果是 "c0VQNVJDV21kUWRQWW8vZWVXVkl3dWZPTjExZ21iUG1VQUI0c0EwaktVaz0="

还有另一段PHP代码

$rawData = openssl_encrypt(pad("asa masa"), 'AES-128-ECB', '4452038393672345');

返回的结果是 "sEP5RCWmdQdPYo/eeWVIwufON11gmbPmUAB4sA0jKUk="

如果有人感兴趣,可以这样做,一切都很好

If anyone is interested, do it this way and everything is fine
$encrypted = openssl_encrypt(pad("asa masa"), 'AES-128-ECB', '4452038393672345',OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY | OPENSSL_ZERO_PADDING);
$encrypted=base64_encode($encrypted);

结果是 "sEP5RCWmdQdPYo/eeWVIwg=="

谢谢

1 个回答

-2

如果我们查看这个链接:https://www.php.net/manual/en/function.openssl-encrypt.php

这里的选项是两个标志的位运算结果,分别是 OPENSSL_RAW_DATA 和 OPENSSL_ZERO_PADDING。

OPENSSL_RAW_DATA = 1(二进制表示为 01

OPENSSL_ZERO_PADDING = 2(二进制表示为 10

所以:

base64_encode(openssl_encrypt($plainText, $cipher, $key, 0)); // 00 = no flags
c0VQNVJDV21kUWRQWW8vZWVXVkl3dWZPTjExZ21iUG1VQUI0c0EwaktVaz0=

base64_encode(openssl_encrypt($plainText, $cipher, $key, 1)); // 01 = OPENSSL_RAW_DATA
sEP5RCWmdQdPYo/eeWVIwufON11gmbPmUAB4sA0jKUk=

base64_encode(openssl_encrypt($plainText, $cipher, $key, 2)); // 10 = OPENSSL_ZERO_PADDING
c0VQNVJDV21kUWRQWW8vZWVXVkl3Zz09

base64_encode(openssl_encrypt($plainText, $cipher, $key, 3)); // 11 = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
sEP5RCWmdQdPYo/eeWVIwg==

这个模式会重复,因为我们只关注最后两位

base64_encode(openssl_encrypt($plainText, $cipher, $key, 4)); // (1)00
c0VQNVJDV21kUWRQWW8vZWVXVkl3dWZPTjExZ21iUG1VQUI0c0EwaktVaz0=

base64_encode(openssl_encrypt($plainText, $cipher, $key, 5)); // (1)01
sEP5RCWmdQdPYo/eeWVIwufON11gmbPmUAB4sA0jKUk=

base64_encode(openssl_encrypt($plainText, $cipher, $key, 6)); // (1)10
c0VQNVJDV21kUWRQWW8vZWVXVkl3Zz09

base64_encode(openssl_encrypt($plainText, $cipher, $key, 7)); // (1)11
sEP5RCWmdQdPYo/eeWVIwg==

OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY | OPENSSL_ZERO_PADDING = 1 | 4 | 2 = 7

所以我们可以这样调用:openssl_encrypt($plainText, $cipher, $key, 7)

默认情况下,openssl_encrypt 使用 PKCS#7 填充,这会导致“重复填充”(你的零填充 + PKCS),这就是为什么在你的情况下应该使用 OPENSSL_ZERO_PADDING(建议使用默认的 openssl_encrypt 填充方式)

如果你访问这个网站:https://www.base64decode.org/,你会看到

c0VQNVJDV21kUWRQWW8vZWVXVkl3dWZPTjExZ21iUG1VQUI0c0EwaktVaz0= 是经过 base64 编码的 sEP5RCWmdQdPYo/eeWVIwufON11gmbPmUAB4sA0jKUk=,而 sEP5RCWmdQdPYo/eeWVIwufON11gmbPmUAB4sA0jKUk= 其实就是 sEP5RCWmdQdPYo/eeWVIwg== 加了一些额外的内容,这些是由于重复填充造成的。

撰写回答