Openssl open: примеры (PHP)

Работа с гибридным шифрованием через openssl_open
Раздел: Шифрование (OpenSSL)
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_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)

Передача некорректного или измененного вектора инициализации для алгоритмов, которые его используют.

// Шифрование и расшифровка с разными 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); // false
bool(false)

Изменения в версиях PHP

PHP 8.0

Параметр private_key теперь ожидает экземпляр OpenSSLAsymmetricKey или OpenSSLCertificate. Ранее использовался ресурс (resource) типа OpenSSL key. Также допускаются строка или массив для обратной совместимости.

PHP 7.0

Добавлена поддержка передачи вектора инициализации iv.

Расширенные примеры

Расшифровка для нескольких получателей

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

Пример php
// Генерация ключевых пар для двух получателей
$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 работают с данными целиком, для больших объемов рекомендуется использовать гибридную схему вручную: асимметрично зашифровать случайный сеансовый ключ, а данные шифровать симметрично по частям.

Пример php
// Генерация ключей
$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)

В 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'
JavaScript (Node.js crypto)

В 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

PHP openssl_open function comments

En
Openssl open Open sealed data