Расшифровать AES-256-CBC в php

Я шифрую в Python следующим образом:

from Crypto.Cipher import AES
from base64 import b64decode, b64encode

BLOCK_SIZE = 16
pad = lambda s: s + (BLOCK_SIZE - len(s.encode()) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s.encode()) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]


class AESCipher:
    def __init__(self, key: str, iv: str):
        self.key = key
        self.iv = iv

    def encrypt(self, text):
        text = pad(text).encode()
        cipher = AES.new(key=self.key.encode(), mode=AES.MODE_CBC, iv=self.iv.encode())
        encrypted_text = cipher.encrypt(text)
        enc = b64encode(encrypted_text).decode('utf-8')
        return enc

    def decrypt(self, encrypted_text):
        encrypted_text = b64decode(encrypted_text)
        cipher = AES.new(key=self.key.encode(), mode=AES.MODE_CBC, iv=self.iv.encode())
        decrypted_text = cipher.decrypt(encrypted_text)
        return unpad(decrypted_text).decode('utf-8')


key = '12345hg5bnlg4mtae678900cdy7ta4vy'
iv = '12345hg5bnlg4mtae678900cdy7ta4vy'[:16]
json = '{"email":"[email protected]","password":"secret","firstName":"test","lastName":"test"}'

# Encrypt
cipher = AESCipher(key, iv)

enc = cipher.encrypt(json)

print(enc)

И мне нужно расшифровать в PHP так:

function encrypt($data, $key) {
    $encryptedData = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_ZERO_PADDING, substr($key, 0, 16));
    return base64_encode($encryptedData);
}

function decrypt($encryptedData, $key) {
    $decryptedData = openssl_decrypt(base64_decode($encryptedData), 'AES-256-CBC', $key, OPENSSL_ZERO_PADDING, substr($key, 0, 16));
    return $decryptedData;
}

К сожалению, я получаю пустой результат при расшифровке. Не могли бы вы сказать, где мой баг? Я не получаю никаких ошибок, поэтому трудно понять, в чем проблема.

Спасибо

В коде PHP нельзя отключать заполнение, но необходимо отключить неявную кодировку Base64 (или, альтернативно, необходимо удалить явную кодировку Base64).

Topaco 30.07.2024 14:05

Обратите внимание, что повторное использование пар ключ/IV является уязвимостью и что PyCryptodome поддерживает заполнение PKCS#7 (поэтому специальная реализация не требуется).

Topaco 30.07.2024 14:07

@Topaco Вы правы, теперь это должно работать на Python. Не могли бы вы опубликовать свой код? Мне нужно только зашифровать на Python и расшифровать на PHP с помощью моего собственного ключа.

Rougher 30.07.2024 14:20

Готово, я разместил свои комментарии в качестве ответа.

Topaco 30.07.2024 14:49
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
4
54
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ошибки:

  • Проблема с размером IV: IV (вектор инициализации) должен быть ровно 16 bytes для AES-256-CBC. Если размер IV неправильный, процесс шифрования/дешифрования завершится неудачно.

  • Проблема с заполнением: Использование OPENSSL_ZERO_PADDING требует, чтобы данные были кратны размеру блока (16 байт). В противном случае функция может выйти из строя или дать неправильные результаты. По умолчанию openssl_encrypt и openssl_decrypt корректно обрабатывают отступы, не указывая их.

Решение:

function encrypt($data, $key) {
    $iv = substr($key, 0, 16); // Use a proper IV
    $encryptedData = openssl_encrypt($data, 'AES-256-CBC', $key, 0, $iv);
    return base64_encode($encryptedData);
}

function decrypt($encryptedData, $key) {
    $iv = substr($key, 0, 16); // Use the same IV as in encrypt
    $decryptedData = openssl_decrypt(base64_decode($encryptedData), 'AES-256-CBC', $key, 0, $iv);
    return $decryptedData;
}

Использование:

$data = "Hello, World!";
$key = "12345678901234567890123456789012"; // 32-byte key for AES-256

$encrypted = encrypt($data, $key);
echo "Encrypted: " . $encrypted . "\n";

$decrypted = decrypt($encrypted, $key);
echo "Decrypted: " . $decrypted . "\n";

Ваш код у меня не работает. Обратите внимание, что я шифрую на Python и расшифровываю на PHP. Вы опубликовали оба направления на PHP.

Rougher 30.07.2024 14:21

Также я не понял проблему с размером IV, о которой вы упомянули. Обратите внимание: я делаю то же самое, но в одну строку.

Rougher 30.07.2024 14:22

@Rougher, можешь ли ты поделиться зашифрованными данными, чтобы я мог попытаться расшифровать их на PHP?

Hassaan 30.07.2024 14:43
Ответ принят как подходящий

Запуск вашего кода Python дает следующий зашифрованный текст (в кодировке Base64):

r9Ufy4dnw2Yr0apRe1q0N0eiE+2VEOUbO35CeLQbJia1x8IM66nzweFdnJ32gpQTsZ33As6XjBZUU3bjpZrwyndeTP1ln8890qSyqlmlXOwiYnDHe5Zjg9Ws1F+zR0nl

Чтобы расшифровать это с помощью PHP-кода:

  • заполнение по умолчанию PKCS#7 не должно быть отключено (т.е. удалить опцию OPENSSL_ZERO_PADDING)
  • он не должен быть декодирован Base64 дважды (т. е. удалить явное декодирование Base64 или, альтернативно, применить опцию OPENSSL_RAW_DATA)

Следующий (фиксированный) PHP-код успешно расшифровывает зашифрованный текст:

$key = '12345hg5bnlg4mtae678900cdy7ta4vy';
$ciphertext = 'r9Ufy4dnw2Yr0apRe1q0N0eiE+2VEOUbO35CeLQbJia1x8IM66nzweFdnJ32gpQTsZ33As6XjBZUU3bjpZrwyndeTP1ln8890qSyqlmlXOwiYnDHe5Zjg9Ws1F+zR0nl';

function decrypt($encryptedData, $key) {
    $decryptedData = openssl_decrypt($encryptedData, 'AES-256-CBC', $key, 0, substr($key, 0, 16)); // 1. don't disable padding, 2. remove the explicit Base64 decoding
    return $decryptedData;                       
}
print(decrypt($ciphertext, $key)); // {"email":"[email protected]","password":"secret","firstName":"test","lastName":"test"}

Поскольку повторное использование пар ключ/IV является уязвимостью, более безопасно генерировать случайный IV во время шифрования. IV не является секретным и передается расшифровывающей стороне вместе с зашифрованным текстом (обычно объединенным). Также обратите внимание, что PyCryptodome поддерживает заполнение, см. Crypto.Util.padding, поэтому нет необходимости в специальной реализации.

Это работает, спасибо!

Rougher 30.07.2024 20:14

Другие вопросы по теме