用 PHP 加密文本并用 Python 解密

Aks*_*kur 3 php python encryption pycrypto php-openssl

我正在使用以下代码片段来加密文本PHP7:

$plaintext = "message to be encrypted";
$cipher = "aes-256-cbc";
$ivlen = openssl_cipher_iv_length($cipher);
$iv = "0123456789012345";
$key = "akshayakshayaksh";
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv);
print $ciphertext;
Run Code Online (Sandbox Code Playgroud)

输出:cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo=

现在,当我尝试在其中解密Python3它时,会出现错误:

from Crypto.Cipher import AES
obj2 = AES.new('akshayakshayaksh', AES.MODE_CBC, '0123456789012345')
ciphertext = "cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo="
obj2.decrypt(ciphertext)
Run Code Online (Sandbox Code Playgroud)

回溯(最近一次调用):
文件“<stdin>”,第 1 行,<module>
文件“/anaconda3/lib/python3.6/site-packages/Crypto/Cipher/blockalgo.py”,第 295 行,在解密返回 self._cipher.decrypt(ciphertext) ValueError:输入字符串的长度必须是 16 的倍数

我知道 AES 是一种分组密码算法。但是,我应该如何修复我的 PHP 代码,以便它生成“填充”密码,任何线索?

t.m*_*dam 6

这里的主要问题是您使用了不同的密钥大小。PHP 会openssl_encrypt根据加密算法字符串("aes-256-cbc"在本例中)确定密钥大小,因此它需要 256 位密钥。如果密钥较短,则用零字节填充,因此使用的实际密钥openssl_encrypt是:

"akshayakshayaksh\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
Run Code Online (Sandbox Code Playgroud)

Pycryptodome 根据密钥的实际大小确定密钥大小,因此您的 Python 代码使用 AES-128-CBC。此外,如所提到的评析通过kelalaka,密文是base64编码(openssl_encrypt基于64位编码默认的密文-如果我们使用,我们可以得到的原始字节OPENSSL_RAW_DATA中$options)。Pycryptodome 不解码密文,所以我们必须使用b64decode().

key = b'akshayakshayaksh\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
obj2 = AES.new(key, AES.MODE_CBC, b'0123456789012345')
ciphertext = b"cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo="
print(obj2.decrypt(b64decode(ciphertext)))
#b'message to be encrypted\t\t\t\t\t\t\t\t\t'
Run Code Online (Sandbox Code Playgroud)

\t末尾的额外字符是填充 - CBC 需要填充。Pycryptodome 不会自动删除填充,但它在Crypto.Util.Padding.

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from base64 import b64decode

key = b'akshayakshayaksh\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
obj2 = AES.new(key, AES.MODE_CBC, b'0123456789012345')
ciphertext = b"cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo="
plaintext = obj2.decrypt(b64decode(ciphertext))
plaintext = unpad(plaintext, AES.block_size)
Run Code Online (Sandbox Code Playgroud)

尽管 PHP 的 openssl 接受任意大小的密钥,但最好使用算法字符串中指定的密钥大小,至少可以防止混淆。此外,关键字节应尽可能随机。

正如 Maarten Bodewes 在评论中所指出的,这个密钥使用的字节范围有限,因此它非常弱。此外,它是通过重复一个单词创建的,这使得它容易受到字典攻击(比暴力攻击快得多)。

在 PHP 中,我们可以获得加密安全的随机字节random_bytes(),

$key = random_bytes(32);  
Run Code Online (Sandbox Code Playgroud)

并在 Python 中使用 os.urandom()

key = os.urandom(32)
Run Code Online (Sandbox Code Playgroud)

(您可以使用相同的函数来创建 IV;您不应该使用静态 IV,IV 必须是不可预测的)

您还可以使用 KDF 从您的密码中派生出一个密钥。在这种情况下,重要的是使用随机盐和足够多的迭代次数。PHP 提供了带有该hash_pbkdf2函数的 PBKDF2 算法,以及带有hashlib.pbkdf2_hmac.

  • 哇很好的答案! (2认同)