Openssl open: примеры (PHP)
openssl_open(string $data, string &$output, string $encrypted_key, OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key, string $cipher_algo, string $iv = ?): boolФункция openssl_open() расшифровывает данные, которые были зашифрованы с помощью openssl_seal(). Эта операция использует комбинацию симметричного и асимметричного шифрования. Исходные данные шифруются случайным сеансовым ключом с использованием симметричного алгоритма, после чего сам сеансовый ключ шифруется открытым ключом каждого получателя.
Функция применяется для безопасной передачи информации нескольким адресатам. Отправитель использует openssl_seal(), а каждый получатель — openssl_open() со своим закрытым ключом для расшифровки сеансового ключа и последующего получения данных.
- encrypted_data (string) — зашифрованные данные, полученные от
openssl_seal(). - open_data (string) — переменная, передаваемая по ссылке, в которую будет записан результат расшифровки.
- encrypted_key (string) — зашифрованный сеансовый ключ, также полученный от
openssl_seal(). - private_key (OpenSSLAsymmetricKey|OpenSSLCertificate|array|string) — закрытый ключ получателя, соответствующий одному из открытых ключей, использованных при вызове
openssl_seal(). - cipher_algo (string) — строка с названием алгоритма шифрования. По умолчанию используется
"RC4". Список доступных алгоритмов можно получить с помощьюopenssl_get_cipher_methods(). - iv (string) — вектор инициализации (initialization vector), если он требуется для выбранного алгоритма.
Функция возвращает true при успешной расшифровке или false в случае ошибки.
Примеры использования
Пример демонстрирует процесс шифрования и последующей расшифровки данных.
// Генерация ключевой пары для получателя
$config = array(
"private_key_bits" => 2048,
);
$res_private = openssl_pkey_new($config);
openssl_pkey_export($res_private, $private_key);
$public_key = openssl_pkey_get_details($res_private)["key"];
// Шифрование данных для получателя
$original_data = "Конфиденциальное сообщение";
$sealed = "";
$encrypted_keys = array();
$cipher_algo = "AES-256-CBC";
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_algo));
if (openssl_seal($original_data, $sealed, $encrypted_keys, array($public_key), $cipher_algo, $iv)) {
// Расшифровка данных получателем
$opened = "";
if (openssl_open($sealed, $opened, $encrypted_keys[0], $private_key, $cipher_algo, $iv)) {
echo "Данные успешно расшифрованы.";
} else {
echo "Ошибка расшифровки.";
}
}Данные успешно расшифрованы.
Пример показывает работу с алгоритмом BF-CBC.
$res_private = openssl_pkey_new(["private_key_bits" => 2048]);
openssl_pkey_export($res_private, $private_key);
$public_key = openssl_pkey_get_details($res_private)["key"];
$data = "Тестовые данные";
$sealed = "";
$ekeys = [];
$cipher = "BF-CBC";
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
if (openssl_seal($data, $sealed, $ekeys, [$public_key], $cipher, $iv)) {
$opened = "";
if (openssl_open($sealed, $opened, $ekeys[0], $private_key, $cipher, $iv)) {
echo "Результат: " . $opened;
}
}Результат: Тестовые данные
Альтернативные функции в PHP
Функция openssl_decrypt() выполняет симметричную расшифровку данных, когда ключ известен всем сторонам заранее. Она не использует гибридную схему с сеансовыми ключами. Предпочтительнее для прямого расшифрования данными одним ключом.
Функция openssl_pkey_decrypt() расшифровывает данные, зашифрованные открытым ключом, напрямую с помощью закрытого ключа. Работает только с небольшими объемами данных (ограничение длины ключа). Используется для расшифровки только сеансового ключа в гибридных схемах, тогда как openssl_open() автоматически выполняет всю цепочку.
Типичные ошибки
Использование закрытого ключа, не соответствующего ни одному из открытых ключей, примененных при шифровании.
// Шифрование с одним ключом, расшифровка с другим
$config = ["private_key_bits" => 2048];
$key1 = openssl_pkey_new($config);
openssl_pkey_export($key1, $private_key1);
$public_key1 = openssl_pkey_get_details($key1)["key"];
$key2 = openssl_pkey_new($config);
openssl_pkey_export($key2, $private_key2); // Другой ключ
$data = "test";
$sealed = "";
$ekeys = [];
$iv = openssl_random_pseudo_bytes(16);
openssl_seal($data, $sealed, $ekeys, [$public_key1], "AES-128-CBC", $iv);
$opened = "";
$result = openssl_open($sealed, $opened, $ekeys[0], $private_key2, "AES-128-CBC", $iv); // Ошибка
var_dump($result); // false
var_dump(openssl_error_string());bool(false) string(75) "error:0407008A:rsa routines:RSA_padding_check_PKCS1_type_2:invalid padding"
Передача некорректного или измененного вектора инициализации для алгоритмов, которые его используют.
// Шифрование и расшифровка с разными IV
$res_private = openssl_pkey_new(["private_key_bits" => 2048]);
openssl_pkey_export($res_private, $private_key);
$public_key = openssl_pkey_get_details($res_private)["key"];
$data = "message";
$sealed = "";
$ekeys = [];
$iv1 = openssl_random_pseudo_bytes(16);
$iv2 = openssl_random_pseudo_bytes(16); // Другой IV
openssl_seal($data, $sealed, $ekeys, [$public_key], "AES-256-CBC", $iv1);
$opened = "";
$result = openssl_open($sealed, $opened, $ekeys[0], $private_key, "AES-256-CBC", $iv2);
var_dump($result); // falsebool(false)
Изменения в версиях PHP
Параметр private_key теперь ожидает экземпляр OpenSSLAsymmetricKey или OpenSSLCertificate. Ранее использовался ресурс (resource) типа OpenSSL key. Также допускаются строка или массив для обратной совместимости.
Добавлена поддержка передачи вектора инициализации iv.
Расширенные примеры
Пример показывает, как данные, зашифрованные для нескольких получателей, могут быть расшифрованы каждым из них с помощью своего закрытого ключа.
// Генерация ключевых пар для двух получателей
$config = ["private_key_bits" => 2048];
$keyPair1 = openssl_pkey_new($config);
openssl_pkey_export($keyPair1, $privKey1);
$pubKey1 = openssl_pkey_get_details($keyPair1)["key"];
$keyPair2 = openssl_pkey_new($config);
openssl_pkey_export($keyPair2, $privKey2);
$pubKey2 = openssl_pkey_get_details($keyPair2)["key"];
// Шифрование данных для обоих получателей
$secret = "Информация для двух адресатов";
$sealed = "";
$encryptedKeys = [];
$cipher = "AES-256-CTR";
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
if (openssl_seal($secret, $sealed, $encryptedKeys, [$pubKey1, $pubKey2], $cipher, $iv)) {
// Получатель 1 расшифровывает
$opened1 = "";
if (openssl_open($sealed, $opened1, $encryptedKeys[0], $privKey1, $cipher, $iv)) {
echo "Получатель 1: $opened1\n";
}
// Получатель 2 расшифровывает
$opened2 = "";
if (openssl_open($sealed, $opened2, $encryptedKeys[1], $privKey2, $cipher, $iv)) {
echo "Получатель 2: $opened2\n";
}
}Получатель 1: Информация для двух адресатов Получатель 2: Информация для двух адресатов
Поскольку openssl_seal/open работают с данными целиком, для больших объемов рекомендуется использовать гибридную схему вручную: асимметрично зашифровать случайный сеансовый ключ, а данные шифровать симметрично по частям.
// Генерация ключей
$res_private = openssl_pkey_new(["private_key_bits" => 2048]);
openssl_pkey_export($res_private, $private_key);
$public_key = openssl_pkey_get_details($res_private)["key"];
// Создание сеансового ключа и IV
$sessionKey = openssl_random_pseudo_bytes(32); // для AES-256
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("AES-256-CBC"));
$cipher = "AES-256-CBC";
// Асимметричное шифрование сеансового ключа (имитация openssl_seal для ключа)
openssl_public_encrypt($sessionKey, $encryptedKey, $public_key, OPENSSL_PKCS1_OAEP_PADDING);
// Симуляция шифрования большого файла по частям (только для демонстрации)
$dataChunks = ["Часть1", "Часть2", "Часть3"];
$encryptedChunks = [];
foreach ($dataChunks as $chunk) {
$encryptedChunks[] = openssl_encrypt($chunk, $cipher, $sessionKey, 0, $iv);
}
// Расшифровка сеансового ключа (аналог openssl_open для ключа)
openssl_private_decrypt($encryptedKey, $decryptedSessionKey, $private_key, OPENSSL_PKCS1_OAEP_PADDING);
// Расшифровка данных по частям
foreach ($encryptedChunks as $encChunk) {
echo openssl_decrypt($encChunk, $cipher, $decryptedSessionKey, 0, $iv);
}Часть1Часть2Часть3
Альтернативы в других языках
В Python для реализации похожей гибридной схемы используют библиотеку cryptography. Аналогом является комбинация шифрования сеансового ключа асимметричным методом и данных симметричным.
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import os
# Генерация ключей и данных
private_key = serialization.load_pem_private_key(private_pem, password=None)
public_key = private_key.public_key()
data = b"Secret message"
# Симметричное шифрование данных
session_key = os.urandom(32)
iv = os.urandom(16)
cipher = Cipher(algorithms.AES(session_key), modes.CBC(iv))
encryptor = cipher.encryptor()
encrypted_data = encryptor.update(data) + encryptor.finalize()
# Асимметричное шифрование сеансового ключа
encrypted_key = public_key.encrypt(
session_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# Расшифровка (аналог openssl_open)
decrypted_key = private_key.decrypt(
encrypted_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
cipher = Cipher(algorithms.AES(decrypted_key), modes.CBC(iv))
decryptor = cipher.decryptor()
decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()
print(decrypted_data)b'Secret message'
В Node.js модуль crypto предоставляет методы для гибридного шифрования, но прямой аналог openssl_open отсутствует. Реализация требует разделения шагов.
const crypto = require('crypto');
const { publicEncrypt, privateDecrypt } = crypto;
// Генерация ключей
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
});
const data = 'Confidential data';
// Симметричное шифрование
const sessionKey = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', sessionKey, iv);
let encryptedData = cipher.update(data, 'utf8', 'hex');
encryptedData += cipher.final('hex');
// Асимметричное шифрование сеансового ключа
const encryptedKey = publicEncrypt(publicKey, sessionKey);
// Расшифровка
const decryptedKey = privateDecrypt(privateKey, encryptedKey);
const decipher = crypto.createDecipheriv('aes-256-cbc', decryptedKey, iv);
let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);Confidential data