Python-to-PHP 与 openssl_encrypt AES-CBC 兼容的 AES 加密
A Python-to-PHP compatible AES encryption with openssl_encrypt AES-CBC
我有一个 Python 应用程序和 PHP 网站,它们通过某些特定的网络层发送消息进行通信。我的任务是使用该通道发送所有经过 AES 加密和 base64 编码的消息。双方手动预共享加密密钥。
在我的 PHP 中,我使用此代码创建了名为 $payload
:
的最终消息文本
$key = substr('abdsbfuibewuiuizasbfeuiwhfashgfhj56urfgh56rt7856rh', 0, 32);
$magic = 'THISISANENCRYPTEDMESSAGE';
function crypted($data) {
global $key, $magic;
// serialize
$payload = json_encode($data);
// encrypt and get base64 string with padding (==):
$payload = @openssl_encrypt($payload, 'AES-192-CBC', $key);
// prepend with magic
$payload = $magic.$payload;
return $payload;
}
我在 Python 应用程序中收到这样的消息,剥离魔法,获取 base64 字节数据。我找不到样本来制作兼容的 AES 密码来解码此消息的问题。
Key 和 "Magic" 只是预先共享并且双方都知道的值,这是正确的吗?我需要静脉注射吗?
这是来自 SO 的 Python 解决方案,它不适用于我的加密消息。
from base64 import b64encode, b64decode
from Crypto.Cipher import AES
class AESCipher:
class InvalidBlockSizeError(Exception):
"""Raised for invalid block sizes"""
pass
def __init__(self, key):
self.key = key
self.iv = bytes(key[0:16], 'utf-8')
def __pad(self, text):
text_length = len(text)
amount_to_pad = AES.block_size - (text_length % AES.block_size)
if amount_to_pad == 0:
amount_to_pad = AES.block_size
pad = chr(amount_to_pad)
return text + pad * amount_to_pad
def __unpad(self, text):
pad = ord(text[-1])
return text[:-pad]
def encrypt( self, raw ):
raw = self.__pad(raw)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
return b64encode(cipher.encrypt(raw))
def decrypt( self, enc ):
enc = b64decode(enc)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv )
r = cipher.decrypt(enc) # type: bytes
return self.__unpad(r.decode("utf-8", errors='strict'))
它在最后一行因解码问题而失败。 "ignore" 解码模式 returns 空字符串。
# with magic: "THISISANENCRYPTEDMESSAGE8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI="
# contains: {'test': 'hello world'}
payload = '8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI='
aes = AESCipher('abdsbfuibewuiuizasbfeuiwhfashgfh')
print(aes.decrypt(payload))
加薪:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "../test.py", line 36, in decrypt
return self.__unpad(cipher.decrypt(enc).decode("utf-8"))
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x9e in position 0: invalid start byte
我错过了什么?
您正在使用 Cipher Block Chaining,但没有将 IV 传递给 openssl_encrypt()
;这意味着 IV 是 NUL 字节的 16 倍。但是您的 Python 代码使用 key 作为 IV,因此会产生完全不同的解密结果。
接下来,您选择了 AES-192-CBC
,而不是 AES-256-CBC
,因此密钥只使用了 192 位。 192 位 == 24 字节,不是你想的 32。
您还需要完全删除 __unpad()
调用,您的加密数据中没有填充,在解密之前从末尾删除数据只会导致解密失败。
所以要在Python这边解密,密钥使用24个字符,给出一个16倍的IV\x00
,然后传入all 你从 Base64 解码的数据:
>>> from Crypto.Cipher import AES
>>> from base64 import b64decode
>>> key = 'abdsbfuibewuiuizasbfeuiwhfashgfh'[:24]
>>> key
'abdsbfuibewuiuizasbfeuiw'
>>> payload = '8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI='
>>> enc = b64decode(payload)
>>> cipher = AES.new(key, AES.MODE_CBC, '\x00' * 16)
>>> cipher.decrypt(enc)
b'{"test":"hello world"}\n\n\n\n\n\n\n\n\n\n'
如果您想使用密钥的完整 32 个字符,请改用 AES-256-CBC。
您确实想要生成一个随机 IV,以便窥探流量的人无法确定模式(其中相同的有效负载每次都会生成相同的加密消息)。生成 IV,将其包含在您发送的数据中,并在 Python 端提取它以传递给 AES.new()
函数。
我有一个 Python 应用程序和 PHP 网站,它们通过某些特定的网络层发送消息进行通信。我的任务是使用该通道发送所有经过 AES 加密和 base64 编码的消息。双方手动预共享加密密钥。
在我的 PHP 中,我使用此代码创建了名为 $payload
:
$key = substr('abdsbfuibewuiuizasbfeuiwhfashgfhj56urfgh56rt7856rh', 0, 32);
$magic = 'THISISANENCRYPTEDMESSAGE';
function crypted($data) {
global $key, $magic;
// serialize
$payload = json_encode($data);
// encrypt and get base64 string with padding (==):
$payload = @openssl_encrypt($payload, 'AES-192-CBC', $key);
// prepend with magic
$payload = $magic.$payload;
return $payload;
}
我在 Python 应用程序中收到这样的消息,剥离魔法,获取 base64 字节数据。我找不到样本来制作兼容的 AES 密码来解码此消息的问题。
Key 和 "Magic" 只是预先共享并且双方都知道的值,这是正确的吗?我需要静脉注射吗?
这是来自 SO 的 Python 解决方案,它不适用于我的加密消息。
from base64 import b64encode, b64decode
from Crypto.Cipher import AES
class AESCipher:
class InvalidBlockSizeError(Exception):
"""Raised for invalid block sizes"""
pass
def __init__(self, key):
self.key = key
self.iv = bytes(key[0:16], 'utf-8')
def __pad(self, text):
text_length = len(text)
amount_to_pad = AES.block_size - (text_length % AES.block_size)
if amount_to_pad == 0:
amount_to_pad = AES.block_size
pad = chr(amount_to_pad)
return text + pad * amount_to_pad
def __unpad(self, text):
pad = ord(text[-1])
return text[:-pad]
def encrypt( self, raw ):
raw = self.__pad(raw)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
return b64encode(cipher.encrypt(raw))
def decrypt( self, enc ):
enc = b64decode(enc)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv )
r = cipher.decrypt(enc) # type: bytes
return self.__unpad(r.decode("utf-8", errors='strict'))
它在最后一行因解码问题而失败。 "ignore" 解码模式 returns 空字符串。
# with magic: "THISISANENCRYPTEDMESSAGE8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI="
# contains: {'test': 'hello world'}
payload = '8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI='
aes = AESCipher('abdsbfuibewuiuizasbfeuiwhfashgfh')
print(aes.decrypt(payload))
加薪:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "../test.py", line 36, in decrypt
return self.__unpad(cipher.decrypt(enc).decode("utf-8"))
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x9e in position 0: invalid start byte
我错过了什么?
您正在使用 Cipher Block Chaining,但没有将 IV 传递给 openssl_encrypt()
;这意味着 IV 是 NUL 字节的 16 倍。但是您的 Python 代码使用 key 作为 IV,因此会产生完全不同的解密结果。
接下来,您选择了 AES-192-CBC
,而不是 AES-256-CBC
,因此密钥只使用了 192 位。 192 位 == 24 字节,不是你想的 32。
您还需要完全删除 __unpad()
调用,您的加密数据中没有填充,在解密之前从末尾删除数据只会导致解密失败。
所以要在Python这边解密,密钥使用24个字符,给出一个16倍的IV\x00
,然后传入all 你从 Base64 解码的数据:
>>> from Crypto.Cipher import AES
>>> from base64 import b64decode
>>> key = 'abdsbfuibewuiuizasbfeuiwhfashgfh'[:24]
>>> key
'abdsbfuibewuiuizasbfeuiw'
>>> payload = '8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI='
>>> enc = b64decode(payload)
>>> cipher = AES.new(key, AES.MODE_CBC, '\x00' * 16)
>>> cipher.decrypt(enc)
b'{"test":"hello world"}\n\n\n\n\n\n\n\n\n\n'
如果您想使用密钥的完整 32 个字符,请改用 AES-256-CBC。
您确实想要生成一个随机 IV,以便窥探流量的人无法确定模式(其中相同的有效负载每次都会生成相同的加密消息)。生成 IV,将其包含在您发送的数据中,并在 Python 端提取它以传递给 AES.new()
函数。