Шифрование текста в PHP и дешифрование в 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;

Выходные данные: cUXDhOEGz19QEo9XDvMzXkGFmg / YQUnXEqKVpfYtUGo =

Теперь, когда я пытаюсь расшифровать это в Python3 это дает ошибку:

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

Traceback (последний вызов был последним):
Файл «<STDIN>», строка 1, в <модуль>

файл
«/Anaconda3/lib/python3.6/site-packages/Crypto/Cipher/blockalgo.py»,
строка 295, в расшифровке
return self._cipher.decrypt (ciphertext) ValueError: длина входных строк должна быть кратна 16

Я понял, что AES — это алгоритм блочного шифрования. Однако, как мне исправить мой PHP-код, чтобы он генерировал «дополненный» шифр, какие-либо подсказки?

2

Решение

Основная проблема заключается в том, что вы используете другой размер ключа. РНР 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"

Pycryptodome определяет размер ключа из фактического размера ключа, поэтому ваш код Python использует AES-128-CBC. Кроме того, как уже упоминалось в коментарии от kelalaka зашифрованный текст закодирован в base64 (openssl_encrypt base64-кодирует зашифрованный текст по умолчанию — мы можем получить необработанные байты, если мы используем 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'

Экстра \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)

Хотя PHP openssl принимает ключи произвольного размера, лучше всего использовать размер ключа, указанный в строке алгоритма, чтобы, по крайней мере, избежать путаницы. Также ключевые байты должны быть как можно более случайными.

Как отметил Мартен Бодевес в Комментарии этот ключ использует ограниченный диапазон байтов и поэтому он очень слабый. Кроме того, он создается повторением слова, что делает его уязвимым для атак по словарю (которые намного быстрее, чем атаки методом перебора).

В PHP мы можем получить криптографически безопасные случайные байты с random_bytes(),

$key = random_bytes(32);

и в Python с os.urandom()

key = os.urandom(32)

(Вы можете использовать те же функции для создания IV; вы не должны использовать статический IV, IV должен быть непредсказуемым)

Вы также можете получить ключ из вашего пароля с помощью KDF. В этом случае важно использовать случайную соль и достаточно большое количество итераций. PHP предоставляет алгоритм PBKDF2 с hash_pbkdf2 функция и Python с hashlib.pbkdf2_hmac.

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]