Я шифрую в 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;
}
К сожалению, я получаю пустой результат при расшифровке. Не могли бы вы сказать, где мой баг? Я не получаю никаких ошибок, поэтому трудно понять, в чем проблема.
Спасибо
Обратите внимание, что повторное использование пар ключ/IV является уязвимостью и что PyCryptodome поддерживает заполнение PKCS#7 (поэтому специальная реализация не требуется).
@Topaco Вы правы, теперь это должно работать на Python. Не могли бы вы опубликовать свой код? Мне нужно только зашифровать на Python и расшифровать на PHP с помощью моего собственного ключа.
Готово, я разместил свои комментарии в качестве ответа.
Ошибки:
Проблема с размером 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.
Также я не понял проблему с размером IV, о которой вы упомянули. Обратите внимание: я делаю то же самое, но в одну строку.
@Rougher, можешь ли ты поделиться зашифрованными данными, чтобы я мог попытаться расшифровать их на PHP?
Запуск вашего кода Python дает следующий зашифрованный текст (в кодировке Base64):
r9Ufy4dnw2Yr0apRe1q0N0eiE+2VEOUbO35CeLQbJia1x8IM66nzweFdnJ32gpQTsZ33As6XjBZUU3bjpZrwyndeTP1ln8890qSyqlmlXOwiYnDHe5Zjg9Ws1F+zR0nl
Чтобы расшифровать это с помощью PHP-кода:
OPENSSL_ZERO_PADDING
)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, поэтому нет необходимости в специальной реализации.
Это работает, спасибо!
В коде PHP нельзя отключать заполнение, но необходимо отключить неявную кодировку Base64 (или, альтернативно, необходимо удалить явную кодировку Base64).