Openssl pkey derive: примеры (PHP)

Полное руководство по openssl_pkey_derive в PHP
Раздел: Шифрование (OpenSSL)
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 как более универсальный вариант.

sodium_crypto_scalarmult

Функция из расширения 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); // false
bool(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

PHP 8.0

Тип параметров и возвращаемого значения стал более строгим. Аргументы теперь ожидают объекты OpenSSLAsymmetricKey или OpenSSLCertificate вместо ресурсов. Функция теперь выбрасывает исключение при ошибках, а не возвращает false.

// PHP 8.0+ - обработка ошибок через исключения
try {
    $shared_secret = openssl_pkey_derive($public_key, $private_key);
} catch (\OpenSSLAsymmetricKeyError $e) {
    echo 'Ошибка: ' . $e->getMessage();
}

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

Использование с эллиптическими кривыми (ECDH)
Пример php
// Генерация 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... (сокращено)
Использование с паролями и KDF
Пример php
// Деривация ключа с последующим применением 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);
Работа с различными форматами ключей
Пример php
// Загрузка ключей из различных источников
// Из файла 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);

Аналоги в других языках

Python (cryptography)
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
JavaScript (WebCrypto API)
// Генерация ключей 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"]
);

PHP openssl_pkey_derive function comments

En
Openssl pkey derive Computes shared secret for public value of remote and local DH or ECDH key