Start · Sprachen · PHP · Referenz · openssl_pkey_derive

openssl_pkey_derive

Funktion

Berechnet das gemeinsame Geheimnis (Shared Secret) aus einem öffentlichen und einem privaten DH- oder ECDH-Schlüssel.

seit PHP 7.3.0 Kategorie: crypto

Signatur

openssl_pkey_derive(OpenSSLAsymmetricKey|array|string $public_key, OpenSSLAsymmetricKey|array|string $private_key, int $key_length = 0): string|false

Beschreibung

openssl_pkey_derive implementiert den Diffie-Hellman-Schlüsselaustausch (DH) und dessen elliptische-Kurven-Variante (ECDH). Die Funktion erwartet den öffentlichen Schlüssel der Gegenseite und den eigenen privaten Schlüssel und leitet daraus ein gemeinsames Geheimnis ab, das beiden Parteien bekannt ist, ohne es jemals übertragen zu müssen.

Das zurückgegebene Shared Secret ist ein rohes Byte-Array und sollte niemals direkt als Verschlüsselungsschlüssel verwendet werden. Stattdessen muss es durch eine geeignete Key-Derivation-Function (KDF) wie hash_hkdf() oder hash_pbkdf2() geleitet werden, um daraus einen sicheren symmetrischen Schlüssel abzuleiten.

Die Funktion unterstützt sowohl klassische DH-Parameter-Schlüssel als auch ECDH-Schlüssel auf benannten Kurven (z. B. prime256v1, secp384r1, X25519). Der optionale Parameter key_length erlaubt es, die Länge des zurückgegebenen Geheimnisses zu begrenzen, sofern die zugrundeliegende Implementierung dies unterstützt.

Typische Einsatzgebiete sind der Schlüsselaustausch in sicheren Kommunikationsprotokollen, Ende-zu-Ende-Verschlüsselung und hybride Kryptosysteme, bei denen asymmetrische Verfahren nur zur Aushandlung eines symmetrischen Sitzungsschlüssels genutzt werden.

Parameter

Name Typ Default Beschreibung
$public_key Pflicht OpenSSLAsymmetricKey|array|string Der öffentliche Schlüssel der Gegenseite als OpenSSLAsymmetricKey-Ressource, als Array mit Schlüsselparametern oder als PEM-kodierter String.
$private_key Pflicht OpenSSLAsymmetricKey|array|string Der eigene private Schlüssel als OpenSSLAsymmetricKey-Ressource, als Array mit Schlüsselparametern oder als PEM-kodierter String.
$key_length int 0 Gewünschte Länge des abgeleiteten Geheimnisses in Bytes. Bei 0 wird die volle, vom Algorithmus bestimmte Länge zurückgegeben.

Rückgabewert

Typ
string|false
Beschreibung
Gibt das gemeinsame Geheimnis als binären String zurück. Im Fehlerfall (z. B. inkompatible Schlüssel, ungültige Parameter) wird false zurückgegeben.

Beispiele

ECDH-Schlüsselaustausch mit X25519

<?php
// Alice generiert ihr Schlüsselpaar
$aliceKey = openssl_pkey_new([
    'curve_name' => 'X25519',
    'private_key_type' => OPENSSL_KEYTYPE_EC,
]);
$alicePublic = openssl_pkey_get_details($aliceKey)['key']; // PEM-öffentlicher Schlüssel

// Bob generiert sein Schlüsselpaar
$bobKey = openssl_pkey_new([
    'curve_name' => 'X25519',
    'private_key_type' => OPENSSL_KEYTYPE_EC,
]);
$bobPublic = openssl_pkey_get_details($bobKey)['key']; // PEM-öffentlicher Schlüssel

// Beide Seiten berechnen unabhängig dasselbe Shared Secret
$aliceShared = openssl_pkey_derive($bobPublic, $aliceKey);
$bobShared   = openssl_pkey_derive($alicePublic, $bobKey);

// Das Ergebnis muss auf beiden Seiten identisch sein
var_dump($aliceShared === $bobShared); // bool(true)

// Sicheren Schlüssel ableiten (niemals das Rohgeheimnis direkt verwenden!)
$sessionKey = hash_hkdf('sha256', $aliceShared, 32, 'aes-key', '');
echo bin2hex($sessionKey) . PHP_EOL;
bool(true) <32-Byte-Hex-String>

ECDH-Schlüsselaustausch mit prime256v1 und anschließender AES-Verschlüsselung

<?php
// Schlüsselpaar für Partei A
$partyA = openssl_pkey_new([
    'curve_name' => 'prime256v1',
    'private_key_type' => OPENSSL_KEYTYPE_EC,
]);

// Schlüsselpaar für Partei B
$partyB = openssl_pkey_new([
    'curve_name' => 'prime256v1',
    'private_key_type' => OPENSSL_KEYTYPE_EC,
]);

$pubA = openssl_pkey_get_details($partyA)['key'];
$pubB = openssl_pkey_get_details($partyB)['key'];

// Shared Secret berechnen
$sharedSecret = openssl_pkey_derive($pubB, $partyA);
if ($sharedSecret === false) {
    throw new RuntimeException('Schlüsselableitung fehlgeschlagen.');
}

// KDF: HKDF mit SHA-256, 256-Bit-Schlüssel für AES-256-GCM
$aesKey = hash_hkdf('sha256', $sharedSecret, 32, 'encryption');
$iv     = random_bytes(12);

$plaintext  = 'Geheime Nachricht';
$ciphertext = openssl_encrypt($plaintext, 'aes-256-gcm', $aesKey, OPENSSL_RAW_DATA, $iv, $tag);

echo 'Verschlüsselt: ' . bin2hex($ciphertext) . PHP_EOL;
echo 'Tag: ' . bin2hex($tag) . PHP_EOL;

// Entschlüsseln (Gegenseite würde denselben $aesKey ableiten)
$decrypted = openssl_decrypt($ciphertext, 'aes-256-gcm', $aesKey, OPENSSL_RAW_DATA, $iv, $tag);
echo 'Entschlüsselt: ' . $decrypted . PHP_EOL;
Verschlüsselt: <Hex-String> Tag: <Hex-String> Entschlüsselt: Geheime Nachricht

// Wichtig · Fallstricke

Sicherheitshinweise:

  • Das von openssl_pkey_derive zurückgegebene Rohgeheimnis darf niemals direkt als Verschlüsselungsschlüssel verwendet werden. Leite daraus immer mit einer KDF wie hash_hkdf() einen Schlüssel ab.
  • Stelle sicher, dass der öffentliche Schlüssel der Gegenseite authentifiziert ist (z. B. durch ein Zertifikat oder einen separaten Authentifizierungskanal), da ECDH ohne Authentifizierung anfällig für Man-in-the-Middle-Angriffe ist.
  • Verwende moderne, sichere Kurven wie X25519, X448, prime256v1 oder secp384r1. Veraltete Kurven (z. B. secp112r1) bieten keine ausreichende Sicherheit.
  • Die Funktion gibt false zurück und erzeugt keine Exception — prüfe den Rückgabewert immer explizit.
  • Klassisches DH (nicht EC) ist möglich, aber ECDH ist in der Regel effizienter und wird bevorzugt.