Openssl pkey derive: примеры (PHP)
openssl_pkey_derive(OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $public_key, OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key, int $key_length = 0): string|falseФункция openssl_pkey_derive используется для вычисления общего секрета по схеме Диффи-Хеллмана или на основе эллиптических кривых (ECDH). Она позволяет двум сторонам, имеющим пары открытый/закрытый ключ, получить общий секретный ключ, не передавая его по сети.
Функция применяется в протоколах защищенной связи (например, TLS), системах обмена ключами и криптографических протоколах, где требуется безопасный обмен секретными данными.
- public_key - открытый ключ другой стороны. Может быть ресурсом (resource), объектом OpenSSLAsymmetricKey (PHP 8.0+) или строкой PEM.
- private_key - локальный закрытый ключ. Аналогично, может быть ресурсом, объектом OpenSSLAsymmetricKey или строкой PEM.
- key_length (необязательный) - желаемая длина производного ключа в байтах. Если не указана, используется максимально возможная длина для выбранного алгоритма.
Функция возвращает строку с общим секретом или false в случае ошибки.
Примеры использования
// Генерация пар ключей для двух сторон
$config = array(
"digest_alg" => "sha256",
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_DH
);
// Генерация ключей для Алисы
$alice_key = openssl_pkey_new($config);
openssl_pkey_export($alice_key, $alice_private_pem);
$alice_details = openssl_pkey_get_details($alice_key);
$alice_public_pem = $alice_details['key'];
// Генерация ключей для Боба
$bob_key = openssl_pkey_new($config);
openssl_pkey_export($bob_key, $bob_private_pem);
$bob_details = openssl_pkey_get_details($bob_key);
$bob_public_pem = $bob_details['key'];
// Алиса вычисляет общий секрет с публичным ключом Боба
$alice_shared = openssl_pkey_derive($bob_public_pem, $alice_key);
// Боб вычисляет общий секрет с публичным ключом Алисы
$bob_shared = openssl_pkey_derive($alice_public_pem, $bob_key);
echo 'Секреты совпадают: ' . ($alice_shared === $bob_shared ? 'Да' : 'Нет');Секреты совпадают: Да
Альтернативные функции в PHP
Специализированная функция для вычисления общего ключа по схеме Диффи-Хеллмана. Работает только с DH-ключами, тогда как openssl_pkey_derive поддерживает и ECDH. Рекомендуется использовать openssl_pkey_derive как более универсальный вариант.
Функция из расширения libsodium для криптографии на эллиптических кривых Curve25519. Обеспечивает высокую производительность и безопасность. Предпочтительна для новых проектов с поддержкой современных алгоритмов.
Типичные ошибки
// Попытка использовать RSA ключи для обмена
$rsa_config = array(
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA
);
$key1 = openssl_pkey_new($rsa_config);
$key2 = openssl_pkey_new($rsa_config);
$result = openssl_pkey_derive(
openssl_pkey_get_details($key2)['key'],
$key1
);
var_dump($result); // falsebool(false)
// Передача ключа в неправильном формате
$invalid_key = "NOT A VALID KEY";
$private_key = openssl_pkey_new(array(
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_DH
));
$result = openssl_pkey_derive($invalid_key, $private_key);
if ($result === false) {
echo 'Ошибка: ' . openssl_error_string();
}Ошибка: error:0909006C:PEM routines:get_name:no start line
Изменения в версиях PHP
Тип параметров и возвращаемого значения стал более строгим. Аргументы теперь ожидают объекты OpenSSLAsymmetricKey или OpenSSLCertificate вместо ресурсов. Функция теперь выбрасывает исключение при ошибках, а не возвращает false.
// PHP 8.0+ - обработка ошибок через исключения
try {
$shared_secret = openssl_pkey_derive($public_key, $private_key);
} catch (\OpenSSLAsymmetricKeyError $e) {
echo 'Ошибка: ' . $e->getMessage();
}Расширенные примеры
// Генерация ECDH ключей на кривой prime256v1
$config = array(
"curve_name" => "prime256v1",
"private_key_type" => OPENSSL_KEYTYPE_EC
);
$alice_key = openssl_pkey_new($config);
$bob_key = openssl_pkey_new($config);
// Получение публичных ключей
$alice_details = openssl_pkey_get_details($alice_key);
$bob_details = openssl_pkey_get_details($bob_key);
// Вычисление общего секрета с указанием длины
$shared_secret = openssl_pkey_derive(
$bob_details['key'],
$alice_key,
32 // 256 бит
);
echo 'Длина общего секрета: ' . strlen($shared_secret) . ' байт';
echo 'Хеш секрета: ' . bin2hex(hash('sha256', $shared_secret, true));Длина общего секрета: 32 байт Хеш секрета: 7f3b4a8c... (сокращено)
// Деривация ключа с последующим применением HKDF
$shared_secret = openssl_pkey_derive($public_key, $private_key);
// Использование HKDF для получения ключа фиксированной длины
$salt = random_bytes(16);
$info = 'myapp-v1';
$derived_key = hash_hkdf('sha256', $shared_secret, 32, $info, $salt);
echo 'Производный ключ: ' . bin2hex($derived_key);// Загрузка ключей из различных источников
// Из файла PEM
$private_pem = file_get_contents('private_key.pem');
$private = openssl_pkey_get_private($private_pem);
// Из файла сертификата
$cert = openssl_x509_read(file_get_contents('certificate.crt'));
$public_from_cert = openssl_pkey_get_public($cert);
// Из строки в формате DER (бинарный)
$der_data = file_get_contents('public_key.der');
$pem = "-----BEGIN PUBLIC KEY-----\n" .
base64_encode($der_data) . "\n-----END PUBLIC KEY-----";
$public_from_der = openssl_pkey_get_public($pem);
// Вычисление общего секрета
$result = openssl_pkey_derive($public_from_cert, $private);Аналоги в других языках
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
# Генерация параметров DH
parameters = dh.generate_parameters(generator=2, key_size=2048)
# Генерация ключей
private_key_a = parameters.generate_private_key()
private_key_b = parameters.generate_private_key()
# Вычисление общего секрета
shared_key_a = private_key_a.exchange(private_key_b.public_key())
shared_key_b = private_key_b.exchange(private_key_a.public_key())
print(shared_key_a == shared_key_b) # True// Генерация ключей ECDH
const aliceKey = await crypto.subtle.generateKey(
{ name: "ECDH", namedCurve: "P-256" },
true,
["deriveKey"]
);
const bobKey = await crypto.subtle.generateKey(
{ name: "ECDH", namedCurve: "P-256" },
true,
["deriveKey"]
);
// Вычисление общего секрета Алисой
const aliceShared = await crypto.subtle.deriveKey(
{ name: "ECDH", public: bobKey.publicKey },
aliceKey.privateKey,
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);