在 PHP 中加密文本并在 Python 中解密
Encrypt text in PHP and decrypt in Python
我正在使用以下代码片段来加密 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;
Output: cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo=
现在,当我尝试在 Python3
中解密时,出现错误:
from Crypto.Cipher import AES
obj2 = AES.new('akshayakshayaksh', AES.MODE_CBC, '0123456789012345')
ciphertext = "cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo="
obj2.decrypt(ciphertext)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File
"/anaconda3/lib/python3.6/site-packages/Crypto/Cipher/blockalgo.py",
line 295, in decrypt
return self._cipher.decrypt(ciphertext) ValueError: Input strings must be a multiple of 16 in length
我知道 AES 是一种分组密码算法。但是,我应该如何修复我的 PHP 代码,以便它生成 "padded" 密码,有什么线索吗?
这里的主要问题是您使用了不同的密钥大小。 PHP 的 openssl_encrypt
根据加密算法字符串(在本例中为 "aes-256-cbc"
)确定密钥大小,因此它需要一个 256 位密钥。如果密钥较短,则用零字节填充,因此 openssl_encrypt
使用的实际密钥是:
"akshayakshayaksh[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]"
Pycryptodome 根据密钥的实际大小确定密钥大小,因此您的 Python 代码使用 AES-128-CBC。此外,正如 kelalaka 在 中提到的,密文是 base64 编码的(openssl_encrypt
base64-默认对密文进行编码 - 如果我们在 [=20] 中使用 OPENSSL_RAW_DATA
,我们可以获得原始字节=]). Pycryptodome 不解码密文,所以我们必须使用 b64decode()
。
key = b'akshayakshayaksh[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]'
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'
末尾额外的 \t
个字符是填充 - CBC 需要填充。 Pycryptodome 不会自动删除填充,但它在 Crypto.Util.Padding
中提供了填充功能。
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from base64 import b64decode
key = b'akshayakshayaksh[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]'
obj2 = AES.new(key, AES.MODE_CBC, b'0123456789012345')
ciphertext = b"cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo="
plaintext = obj2.decrypt(b64decode(ciphertext))
plaintext = unpad(plaintext, AES.block_size)
虽然 PHP 的 openssl 接受任意大小的密钥,但最好使用算法字符串中指定的密钥大小,至少可以防止混淆。此外,密钥字节应尽可能随机。
正如 Maarten Bodewes 在 中指出的那样,此密钥使用的字节范围有限,因此非常脆弱。此外,它是通过重复一个词创建的,这使得它容易受到字典攻击(比暴力攻击快得多)。
在 PHP 中,我们可以使用 random_bytes()
、
获得加密安全的随机字节
$key = random_bytes(32);
并在 Python 中与 os.urandom()
key = os.urandom(32)
(您可以使用相同的函数来创建 IV;您不应该使用静态 IV,IV 必须是不可预测的)
您还可以使用 KDF 从您的密码中导出密钥。在这种情况下,重要的是使用随机盐和足够多的迭代次数。 PHP 使用 hash_pbkdf2
function, and Python with hashlib.pbkdf2_hmac
.
提供 PBKDF2 算法
今天,当一位朋友向我指出 Fernet 图书馆时,我正为此苦苦挣扎。它有 python 和 php 版本,使这变得非常简单和强大。
PHP版本:https://github.com/kelvinmo/fernet-php
<?php
require 'vendor/autoload.php';
use Fernet\Fernet;
$key = '[Base64url encoded fernet key]';
$fernet = new Fernet($key);
$token = $fernet->encode('string message');
$message = $fernet->decode('fernet token');
if ($message === null) {
echo 'Token is not valid';
}
?>
Python: https://pypi.org/project/cryptography/
from cryptography.fernet import Fernet
# Put this somewhere safe!
key = Fernet.generate_key()
f = Fernet(key)
token = f.encrypt(b"A really secret message. Not for prying eyes.")
f.decrypt(token)
print(token)
我正在使用以下代码片段来加密 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;
Output: cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo=
现在,当我尝试在 Python3
中解密时,出现错误:
from Crypto.Cipher import AES
obj2 = AES.new('akshayakshayaksh', AES.MODE_CBC, '0123456789012345')
ciphertext = "cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo="
obj2.decrypt(ciphertext)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/anaconda3/lib/python3.6/site-packages/Crypto/Cipher/blockalgo.py", line 295, in decrypt return self._cipher.decrypt(ciphertext) ValueError: Input strings must be a multiple of 16 in length
我知道 AES 是一种分组密码算法。但是,我应该如何修复我的 PHP 代码,以便它生成 "padded" 密码,有什么线索吗?
这里的主要问题是您使用了不同的密钥大小。 PHP 的 openssl_encrypt
根据加密算法字符串(在本例中为 "aes-256-cbc"
)确定密钥大小,因此它需要一个 256 位密钥。如果密钥较短,则用零字节填充,因此 openssl_encrypt
使用的实际密钥是:
"akshayakshayaksh[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]"
Pycryptodome 根据密钥的实际大小确定密钥大小,因此您的 Python 代码使用 AES-128-CBC。此外,正如 kelalaka 在 openssl_encrypt
base64-默认对密文进行编码 - 如果我们在 [=20] 中使用 OPENSSL_RAW_DATA
,我们可以获得原始字节=]). Pycryptodome 不解码密文,所以我们必须使用 b64decode()
。
key = b'akshayakshayaksh[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]'
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'
末尾额外的 \t
个字符是填充 - CBC 需要填充。 Pycryptodome 不会自动删除填充,但它在 Crypto.Util.Padding
中提供了填充功能。
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from base64 import b64decode
key = b'akshayakshayaksh[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]'
obj2 = AES.new(key, AES.MODE_CBC, b'0123456789012345')
ciphertext = b"cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo="
plaintext = obj2.decrypt(b64decode(ciphertext))
plaintext = unpad(plaintext, AES.block_size)
虽然 PHP 的 openssl 接受任意大小的密钥,但最好使用算法字符串中指定的密钥大小,至少可以防止混淆。此外,密钥字节应尽可能随机。
正如 Maarten Bodewes 在
在 PHP 中,我们可以使用 random_bytes()
、
$key = random_bytes(32);
并在 Python 中与 os.urandom()
key = os.urandom(32)
(您可以使用相同的函数来创建 IV;您不应该使用静态 IV,IV 必须是不可预测的)
您还可以使用 KDF 从您的密码中导出密钥。在这种情况下,重要的是使用随机盐和足够多的迭代次数。 PHP 使用 hash_pbkdf2
function, and Python with hashlib.pbkdf2_hmac
.
今天,当一位朋友向我指出 Fernet 图书馆时,我正为此苦苦挣扎。它有 python 和 php 版本,使这变得非常简单和强大。
PHP版本:https://github.com/kelvinmo/fernet-php
<?php
require 'vendor/autoload.php';
use Fernet\Fernet;
$key = '[Base64url encoded fernet key]';
$fernet = new Fernet($key);
$token = $fernet->encode('string message');
$message = $fernet->decode('fernet token');
if ($message === null) {
echo 'Token is not valid';
}
?>
Python: https://pypi.org/project/cryptography/
from cryptography.fernet import Fernet
# Put this somewhere safe!
key = Fernet.generate_key()
f = Fernet(key)
token = f.encrypt(b"A really secret message. Not for prying eyes.")
f.decrypt(token)
print(token)