PHP7.4:OpenSSL AES-CFB加密不同于Python

PHP7.4:OpenSSL AES-CFB加密不同于Python,python,php,encryption,aes,Python,Php,Encryption,Aes,我正在尝试使用PHP7.4复制一段python代码,该代码使用Pycryptodome进行AES-128-CFB加密。 为此,我使用了PHP的openssl_encrypt内置函数。 我尝试了几个配置参数和循环流化床模式,但我总是得到不同的结果。 我发现pycryptodomes CFB实现似乎使用了8位的段大小,这应该是PHP的openssl实现中的aes-128-cfb8模式 IV被故意固定为0,所以请忽略它不安全的事实 下面是我要复制的代码,后面是尝试用不同方法复制结果的PHP代码。 有些

我正在尝试使用PHP7.4复制一段python代码,该代码使用Pycryptodome进行AES-128-CFB加密。 为此,我使用了PHP的openssl_encrypt内置函数。 我尝试了几个配置参数和循环流化床模式,但我总是得到不同的结果。 我发现pycryptodomes CFB实现似乎使用了8位的段大小,这应该是PHP的openssl实现中的
aes-128-cfb8
模式

IV被故意固定为0,所以请忽略它不安全的事实

下面是我要复制的代码,后面是尝试用不同方法复制结果的PHP代码。 有些东西告诉我这与PHP的“字节处理”有关,因为python在字节字符串(由
.encode('utf-8')
返回)和字符串之间进行区分。 最后,您可以看到两个代码的输出:

Python代码:

导入hashlib
从Crypto.Cipher导入AES
key='testKey'
IV='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
ENC_KEY=hashlib.md5(KEY.encode('utf-8')).hexdigest()
打印('key:'+key+'''))
打印('hashedKey:'+加密键)
obj=AES.new(ENC_KEY.encode(“utf8”)、AES.MODE_CFB、IV.encode(“utf8”))
测试数据=‘测试’
打印(“加密”+“测试”+”)
encData=obj.encrypt(测试数据编码(“utf8”))
打印('encData:'+encData.hex())
PHP代码:

函数encTest($testStr,$ENC_KEY)
{
$iv=hex2bin('00000000000000000000000000');
echo“aes-128-cfb8-1:”.bin2hex(openssl_加密($testStr,'aes-128-cfb8',$ENC_密钥,openssl_原始数据,$iv))。“\n”;
echo“aes-128-cfb1-1:”.bin2hex(openssl_加密($testStr,'aes-128-cfb1',$ENC_密钥,openssl_原始数据,$iv))。“\n”;
echo“aes-128-cfb-1:”.bin2hex(openssl_加密($testStr,'aes-128-cfb',$ENC_密钥,openssl_原始数据,$iv))。“\n”;
回音“\n”;
echo“aes-128-cfb8-2:”.bin2hex(openssl_加密($testStr,'aes-128-cfb8',$ENC_密钥,openssl_原始数据| openssl_零填充,$iv))。“\n”;
echo“aes-128-cfb1-2:”.bin2hex(openssl_加密($testStr,'aes-128-cfb1',$ENC_密钥,openssl_原始_数据| openssl_零填充,$iv))。“\n”;
echo“aes-128-cfb-2:”.bin2hex(openssl加密($testStr,'aes-128-cfb',$ENC_密钥,openssl_原始数据| openssl_零填充,$iv))。“\n”;
回音“\n”;
echo“aes-128-cfb8-3:”.bin2hex(openssl加密(utf8编码($testStr),'aes-128-cfb8',utf8编码($ENC密钥),openssl原始数据| openssl零填充,$iv))。“\n”;
echo“aes-128-cfb1-3:”.bin2hex(openssl加密(utf8编码($testStr),'aes-128-cfb1',utf8编码($ENC密钥),openssl原始数据| openssl零填充,$iv))。“\n”;
echo“aes-128-cfb-3:”.bin2hex(openssl加密(utf8编码($testStr),'aes-128-cfb',utf8编码($ENC密钥),openssl原始数据| openssl零填充,$iv))。“\n”;
回音“\n”;
echo“aes-128-cfb8-4:”.bin2hex(openssl_加密(utf8_编码($testStr),'aes-128-cfb8',utf8_编码($ENC_密钥),openssl_原始数据,$iv))。“\n”;
echo“aes-128-cfb1-4:”.bin2hex(openssl_加密(utf8_编码($testStr),'aes-128-cfb1',utf8_编码($ENC_密钥),openssl_原始数据,$iv))。“\n”;
echo“aes-128-cfb-4:”.bin2hex(openssl_加密(utf8_编码($testStr),'aes-128-cfb',utf8_编码($ENC_密钥),openssl_原始数据,$iv))。“\n”;
回音“\n”;
}
$key=“testKey”;
$ENC_KEY=hash('md5',utf8_encode($KEY));
回显“ENC_KEY:.$ENC_KEY.\n”;
$test=“test”;
回显“加密”.$test.\“\n”;
encTest($test,$ENC_-KEY);
Python输出(
encData
应复制):

PHP输出:

key: "testKey"
hashedKey: 24afda34e3f74e54b61a8e4cbe921650
encrypting "test"

aes-128-cfb8-1: b0016a55
aes-128-cfb1-1: bac44c56
aes-128-cfb-1: b0f1c27a

aes-128-cfb8-2: b0016a55
aes-128-cfb1-2: bac44c56
aes-128-cfb-2: b0f1c27a

aes-128-cfb8-3: b0016a55
aes-128-cfb1-3: bac44c56
aes-128-cfb-3: b0f1c27a

aes-128-cfb8-4: b0016a55
aes-128-cfb1-4: bac44c56
aes-128-cfb-4: b0f1c27a
在PHP代码中(更准确地说,对于
openssl\u encrypt
),明确指定了AES变量,例如,与当前使用
AES-128-…
的情况相同,即PHP使用AES-128。过长的键将被截断,过短的键将填充
0
值。由于PHP代码中的
hash
方法将其结果返回为十六进制字符串,因此16字节MD5哈希由32个字符(32字节)表示,即在当前情况下,PHP使用密钥的前16个字节(AES-128)

Python代码中的
hexdigest
方法也将结果作为十六进制字符串返回。但是,在Python代码中(更准确地说,对于PyCryptodome),AES变量由密钥大小指定,即Python代码使用完整的32字节密钥,因此使用AES-256

不同的密钥和AES变体是产生不同结果的主要原因。要解决此问题,两种代码中必须使用相同的密钥和AES变体:

  • 选项1是在Python代码中也使用AES-128。这可以通过以下更改来实现:

    obj = AES.new(ENC_KEY[:16].encode("utf8"), AES.MODE_CFB, IV.encode("utf8"))
    
    然后输出的
    b0016a55
    aes-128-cfb8
    的PHP代码结果一致

  • 选项2是在PHP代码中也使用AES-256。这可以通过将
    aes-128…
    替换为
    aes-256…
    来实现,然后输出为

    aes-256-cfb8-1: 117c1974
    aes-256-cfb1-1: 54096db1
    aes-256-cfb-1 : 11bfdaa9
    
而且,正如预期的那样,
aes-128-cfb8
的输出
117c1974
与Python代码的原始值匹配


CFB模式将分组密码更改为流密码。因此,
n
位在每个加密步骤中被加密,这被称为
CFBn
。有关s.的确切细节

术语
CFBn
(或
CFBn
)也在PHP中使用,即
CFB1
表示一位的加密,
CFB8
表示8位(=一个字节)和整个块(16个字节)的
CFB
。在Python中,每一步的位数是用指定的

也就是说,PHP中的
..-cfb8
对应物在Python中是
segment_size=8
,PHP中的
..-cfb
对应物在Python中是
segment_size=128

在下文中,假设两个代码中使用相同的密钥和相同的AES变体

由于默认值为
segment\u size=8
,因此Python代码的结果与<
aes-256-cfb8-1: 117c1974
aes-256-cfb1-1: 54096db1
aes-256-cfb-1 : 11bfdaa9