AES-128 OFB使用mcrypt(PHP)和pycryptodome(Python)有所不同

时间:2019-05-05 22:41:04

标签: php python encryption aes

免责声明:此处给出的所有示例都不安全,并且距离良好实践也不远。此处使用的代码旨在用于CTF挑战,并且包含多个漏洞。

这是我真正关心的问题:与使用python 2.7中的Crypto.cipher AES进行加密相比,使用mcrypt_encrypt使用相同的密钥,iv,模式和填充进行加密的结果产生了不同的密码,但是仅当使用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";

1 个答案:

答案 0 :(得分:4)

我不确定为什么您要尝试使用mcrypt或OFB模式做任何事情-两者都是过去大多数密码学家试图忘记的爆炸。还不清楚为什么在填充模式下使用填充,除非您正在解决PyCrypto错误(请参见下文)。


要直接回答您的问题,请参考PHP文档:

  

MCRYPT_MODE_OFB(8位模式下的输出反馈)是一种类似于CFB的流密码模式,但可用于不能容忍错误传播的应用中。建议使用NOFB模式而不是OFB模式。

您可能应该使用:

  

MCRYPT_MODE_NOFB(n位模式下的输出反馈)与OFB模式相当,但是在算法的完整块大小上运行。

其中“ OFB”之前的“ N”是操作模式的块大小。


PyCrypto,PyCryptoDome或OpenSSL不存在此类文档。但是,似乎它们一次处理128位(根据PyCrypto错误报告),因为出于某种原因错误地要求接收完整的纯文本块。


如果在8位或128位模式下使用,

OFB会产生不同的密文-除了第一个字节,它应该是相同的。 8位模式与mcrypt或OFB本身一样,是过去的爆炸。它使用每字节 (!)的完整块加密来减少错误传播。

如果您需要流模式,请使用CTR模式,或者最好使用经过身份验证的密码,例如GCM(在下面使用CTR模式)。这将更快(比8位OFB更快),并且更安全。