AES-128 OFB 使用 mcrypt (PHP) 和 pycryptodome (Python) 的不同之处
AES-128 OFB differs using mcrypt (PHP) and pycryptodome (Python)
免责声明:此处给出的所有示例 都不安全 并且与良好实践相去甚远。此处使用的代码旨在用于 CTF 挑战并包含多个漏洞。
这是我真正关心的问题:使用 mcrypt_encrypt 使用相同的密钥、iv、模式和填充进行加密的结果与使用 Crypto.cipher AES 在 python 2.7,但 仅在使用 OFB 模式时 。这是我的例子:
$key = 'SUPER_SECRET_KEY';
$iv = '0000000000000000';
$data = "this is a test";
$padding = 16 - (strlen($data) % 16);
$data .= str_repeat(chr($padding), $padding);
echo base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_OFB, $iv));
结果是:k8Ss4ytOUNvcG96tr+rHdA==
现在 python 示例:
from Crypto.Cipher import AES
from base64 import b64encode
key = 'SUPER_SECRET_KEY'
iv = '0'*16
data = "this is a test"
padding = 16 - (len(data) % 16)
data += chr(padding)*padding
print(b64encode(AES.new(key, AES.MODE_OFB, iv).encrypt(data)))
结果是:kzFpEHCJB+2k2498DhyAMw==
只发生在OFB模式。如果我将模式更改为 CBC(并且不更改任何其他内容),则两个结果将是相同的。知道发生了什么事吗?
编辑:在 PHP 中使用 openssl_encrypt 得到与 python 代码相同的结果。这让我相信 mcrypt_encrypt.
中存在错误
$key = "SUPER_SECRET_KEY";
$iv = "0000000000000000";
$data = "this is a test";
$padding = 16 - (strlen($data) % 16);
$data .= str_repeat(chr($padding), $padding);
$cipher = openssl_encrypt($data, "aes-128-ofb", $key, $options=OPENSSL_RAW_DATA, $iv);
echo base64_encode($cipher) ."\n";
我不确定您为什么要尝试使用 mcrypt 或 OFB 模式做任何事情 - 两者都是大多数密码学家试图忘记的过去的爆炸。还不清楚为什么要在流模式中使用填充,除非你正在解决 PyCrypto 错误(见下文)。
直接回答你的问题,来自PHP的文档:
MCRYPT_MODE_OFB
(output feedback, in 8-bit mode) is a stream cipher mode comparable to CFB, but can be used in applications where error propagation cannot be tolerated. It is recommended to use NOFB mode rather than OFB mode.
你可能应该使用:
MCRYPT_MODE_NOFB
(output feedback, in n-bit mode) is comparable to OFB mode, but operates on the full block size of the algorithm.
其中"OFB"之前的"N"是操作模式的块大小。
PyCrypto、PyCryptoDome 或 OpenSSL 均不存在此类文档。但是,它们似乎一次处理 128 位(根据 PyCrypto 错误报告),因为出于某种原因它错误地要求接收完整的明文块。
如果在 8 位或 128 位模式下使用,OFB 将产生不同的密文 - 除了第一个字节,它应该是相同的。 8 位模式与 mcrypt 或 OFB 本身一样是过去的爆炸;它使用完整块加密 每字节 (!) 以减少错误传播。
如果您需要流模式,请使用 CTR 模式,或者最好使用经过身份验证的密码,例如 GCM(在下面使用 CTR 模式)。这将更快(比 8 位 OFB)并且更安全。
免责声明:此处给出的所有示例 都不安全 并且与良好实践相去甚远。此处使用的代码旨在用于 CTF 挑战并包含多个漏洞。
这是我真正关心的问题:使用 mcrypt_encrypt 使用相同的密钥、iv、模式和填充进行加密的结果与使用 Crypto.cipher AES 在 python 2.7,但 仅在使用 OFB 模式时 。这是我的例子:
$key = 'SUPER_SECRET_KEY';
$iv = '0000000000000000';
$data = "this is a test";
$padding = 16 - (strlen($data) % 16);
$data .= str_repeat(chr($padding), $padding);
echo base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_OFB, $iv));
结果是:k8Ss4ytOUNvcG96tr+rHdA==
现在 python 示例:
from Crypto.Cipher import AES
from base64 import b64encode
key = 'SUPER_SECRET_KEY'
iv = '0'*16
data = "this is a test"
padding = 16 - (len(data) % 16)
data += chr(padding)*padding
print(b64encode(AES.new(key, AES.MODE_OFB, iv).encrypt(data)))
结果是:kzFpEHCJB+2k2498DhyAMw==
只发生在OFB模式。如果我将模式更改为 CBC(并且不更改任何其他内容),则两个结果将是相同的。知道发生了什么事吗?
编辑:在 PHP 中使用 openssl_encrypt 得到与 python 代码相同的结果。这让我相信 mcrypt_encrypt.
中存在错误$key = "SUPER_SECRET_KEY";
$iv = "0000000000000000";
$data = "this is a test";
$padding = 16 - (strlen($data) % 16);
$data .= str_repeat(chr($padding), $padding);
$cipher = openssl_encrypt($data, "aes-128-ofb", $key, $options=OPENSSL_RAW_DATA, $iv);
echo base64_encode($cipher) ."\n";
我不确定您为什么要尝试使用 mcrypt 或 OFB 模式做任何事情 - 两者都是大多数密码学家试图忘记的过去的爆炸。还不清楚为什么要在流模式中使用填充,除非你正在解决 PyCrypto 错误(见下文)。
直接回答你的问题,来自PHP的文档:
MCRYPT_MODE_OFB
(output feedback, in 8-bit mode) is a stream cipher mode comparable to CFB, but can be used in applications where error propagation cannot be tolerated. It is recommended to use NOFB mode rather than OFB mode.
你可能应该使用:
MCRYPT_MODE_NOFB
(output feedback, in n-bit mode) is comparable to OFB mode, but operates on the full block size of the algorithm.
其中"OFB"之前的"N"是操作模式的块大小。
PyCrypto、PyCryptoDome 或 OpenSSL 均不存在此类文档。但是,它们似乎一次处理 128 位(根据 PyCrypto 错误报告),因为出于某种原因它错误地要求接收完整的明文块。
如果在 8 位或 128 位模式下使用,OFB 将产生不同的密文 - 除了第一个字节,它应该是相同的。 8 位模式与 mcrypt 或 OFB 本身一样是过去的爆炸;它使用完整块加密 每字节 (!) 以减少错误传播。
如果您需要流模式,请使用 CTR 模式,或者最好使用经过身份验证的密码,例如 GCM(在下面使用 CTR 模式)。这将更快(比 8 位 OFB)并且更安全。