Navigation
 Startseite
 Fachbücher
 Anzeigenmarkt
 Forum
 Webmaster News
 Script Newsletter
 Push Meldungen
 Kontakt
 Script Installation
 Php
 Php Tutorials
 Lernpfade
 Webhoster Vergleich
 Impressum

Community-Bereich
 kostenlos Registrieren
 Anmelden
 Benutzerliste

Script Datenbank
 Script Archiv
 Script Top 20
 Screenshots
 Testberichte

Suche
 

Unsere Php Scripts
 Counter Script
 Umfrage Script
 Bilder Upload Script
 Terminverwaltung
 Simple PHP Forum
 RSS Grabber

Tools und Generatoren
 .htpasswd Generator
 md5 Generator
 base64 Generator
 Markdown to HTML
 Colorpicker
 Unix timestamp Tool
 Unit Test Generator
 TLD Liste
 Webkatalog‑Verzeichnis

Artfiles.de
Bietet Serviceorientierte...
https://www.Artfiles.de
Hosterplus.de
Bekommen Sie Speicherplatz (Webspace), Domains...
https://www.Hosterplus.de
 
 
 

PHP openssl_pkey_new(): RSA- und EC-Schlüssel erzeugen

Sie befinden sich: Home > Php Tutorial > PHP openssl_pkey_new(): RSA...

PHP openssl_pkey_new(): RSA- und EC-Schlüssel erzeugen
Veröffentlicht am:
09.09.2026
Hits / Besucher:
8
Sprache:
  Deutsch
Tutorial Art:
eigenes
Eingetragen von:
Merkliste:
 
Beschreibung

Ein Partner will künftig prüfen können, dass ein Webhook wirklich aus Ihrem System stammt. Ein gemeinsames Passwort scheidet aus, weil damit auch der Empfänger Nachrichten fälschen könnte. Auf dem Shared Hosting gibt es keine Shell, das Erzeugen des Schlüssels muss also PHP selbst erledigen.

Illustration zum Tutorial: PHP openssl_pkey_new(): RSA- und EC-Schlüssel erzeugen

Im Bild teilt sich ein Schlüssel in zwei: einer wandert in den Safe, der andere geht nach außen. Genau diese Aufteilung ist der Kern des Verfahrens.

Was PHP openssl_pkey_new() erzeugt

PHP openssl_pkey_new() erzeugt ein vollständiges Schlüsselpaar und gibt es als ein einziges Objekt zurück. Darin stecken beide Hälften: der private Schlüssel, der niemals das Haus verlässt, und der öffentliche Schlüssel, den jeder bekommen darf. Diese Doppelnatur des Rückgabewerts verwirrt am Anfang, denn ein Aufruf liefert scheinbar nur einen Wert.

<?php

$paar = openssl_pkey_new([
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);

if ($paar === false) {
exit('Schluessel konnte nicht erzeugt werden');
}

var_dump($paar instanceof OpenSSLAsymmetricKey); // bool(true)

Seit PHP 8.0 kommt ein Objekt der Klasse OpenSSLAsymmetricKey zurück. Vorher war es eine Resource. Wer älteren Code übernimmt, findet dort häufig eine Prüfung mit is_resource(), und die schlägt seit dem Versionswechsel immer fehl, obwohl der Schlüssel einwandfrei erzeugt wurde. Richtig ist der Vergleich mit false.

Die Signatur im Original

Die englischen Parameternamen aus dem Handbuch tauchen in Konfigurationsdateien und Fehlermeldungen wieder auf, deshalb hier die Beschreibung im Original:

openssl_pkey_new(?array $options = null): OpenSSLAsymmetricKey|false


/* options - configuration for the key generation, e.g. private_key_bits,
private_key_type and digest_alg. Values not set here fall back
to openssl.cnf of the installed openssl version.
return - an object holding the openssl key material, both the private
key and the public part, or false on failure

The same options array is accepted by openssl_csr_new() when a csr is
built from the key. Since PHP 8.0 the return value is an object; in an
older php version it was a resource. */

Der letzte Absatz ist für die Umstellung wichtig: Wer noch mit is_resource() prüft, bekommt seit PHP 8 immer ein negatives Ergebnis.

Der vollständige Ablauf in vier Schritten

Aus dem Objekt, das PHP openssl_pkey_new() zurückgibt, müssen beide Hälften herausgeholt werden, bevor sie sich speichern lassen. Für den privaten Teil ist openssl_pkey_export() zuständig, für den öffentlichen openssl_pkey_get_details(). Beide liefern PEM, also die Textform mit den bekannten Begrenzungszeilen.

<?php

$paar = openssl_pkey_new(['private_key_bits' => 2048]);

/* privaten Teil als PEM in eine Variable schreiben */
openssl_pkey_export($paar, $privatPem);

/* oeffentlichen Teil aus den Details holen */
$details = openssl_pkey_get_details($paar);
$oeffentlichPem = $details['key'];

file_put_contents('/pfad/privat.pem', $privatPem);
file_put_contents('/pfad/oeffentlich.pem', $oeffentlichPem);

echo substr($oeffentlichPem, 0, 27);
/* -----BEGIN PUBLIC KEY----- */

Die Regel dazu ist kurz und wichtig: Nur die Datei mit dem öffentlichen Schlüssel wird weitergegeben. Wer versehentlich den privaten Teil ausliefert, kann das Verfahren sofort einstellen, denn dann kann jeder in seinem Namen unterschreiben.

Den privaten Schlüssel mit einer Passphrase schützen

Das Ergebnis von PHP openssl_pkey_new() landet ohne dritten Parameter im Klartext auf der Platte, denn openssl_pkey_export() verschlüsselt nur, wenn eine Passphrase mitkommt. Auf einem Webserver, auf dem mehrere Projekte liegen, ist das eine schlechte Idee. Die Passphrase verschlüsselt den privaten Schlüssel innerhalb der Datei.

<?php

$paar = openssl_pkey_new(['private_key_bits' => 3072]);
$passphrase = getenv('SCHLUESSEL_PASSWORT');

openssl_pkey_export($paar, $privatPem, $passphrase);
file_put_contents('/pfad/privat.pem', $privatPem);

/* spaeter wieder laden */
$geladen = openssl_pkey_get_private(
file_get_contents('/pfad/privat.pem'),
$passphrase
);

if ($geladen === false) {
exit('Passphrase falsch oder Datei beschaedigt');
}

Die Passphrase gehört nicht in den Quelltext, sondern in eine Umgebungsvariable oder eine Konfigurationsdatei außerhalb des Web-Wurzelverzeichnisses. Damit ist der Schlüssel wenigstens nicht mit einem einzigen Dateizugriff verwertbar.

Signieren und prüfen

Ein Schlüsselpaar allein tut nichts. Der Zweck von PHP openssl_pkey_new() zeigt sich erst, wenn Daten damit unterschrieben und auf der Gegenseite geprüft werden. Das erledigen openssl_sign() und openssl_verify(). Das folgende Beispiel zeigt beide Fälle, den erfolgreichen und den absichtlich fehlschlagenden.

<?php

$paar = openssl_pkey_new(['private_key_bits' => 2048]);
openssl_pkey_export($paar, $privatPem);
$oeffentlich = openssl_pkey_get_details($paar)['key'];

$nachricht = '{"auftrag":4711,"betrag":"19.90"}';

openssl_sign($nachricht, $signatur, $privatPem, OPENSSL_ALGO_SHA256);
$uebertragen = base64_encode($signatur);

/* Empfaengerseite */
$geprueft = openssl_verify(
$nachricht,
base64_decode($uebertragen),
$oeffentlich,
OPENSSL_ALGO_SHA256
);
echo $geprueft; // 1

/* eine veraenderte Nachricht faellt sofort auf */
$manipuliert = '{"auftrag":4711,"betrag":"1.90"}';
echo openssl_verify(
$manipuliert,
base64_decode($uebertragen),
$oeffentlich,
OPENSSL_ALGO_SHA256
); // 0

Der Rückgabewert kennt drei Zustände: 1 für eine gültige Signatur, 0 für eine ungültige und -1 für einen Fehler. Wer nur auf true prüft, hält einen Fehler für eine gültige Unterschrift, denn -1 ist in PHP wahr. Die Prüfung gehört deshalb strikt auf === 1.

Die Optionen im Konfigurationsarray

Das Array, das PHP openssl_pkey_new() entgegennimmt, steuert Verfahren und Stärke. In der Praxis reichen zwei bis drei Einträge, die übrigen sind Sonderfälle.

Option Vorgabe Bedeutung
private_key_bits 2048 Schlüssellänge bei RSA. 2048 ist das Minimum, 3072 die vorsichtigere Wahl. Jede Verdopplung kostet spürbar Rechenzeit beim Erzeugen.
private_key_type RSA OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC, OPENSSL_KEYTYPE_DSA oder OPENSSL_KEYTYPE_DH.
curve_name keine Nur bei EC. Übliche Werte sind prime256v1 und secp384r1.
config Systempfad Pfad zu einer eigenen openssl.cnf. Unter Windows häufig nötig.

Ein Aufruf ganz ohne Array funktioniert und nimmt die Vorgaben. Für Produktivsysteme sollte die Schlüssellänge trotzdem ausdrücklich dastehen, damit sie beim nächsten Serverumzug nicht stillschweigend eine andere wird.

RSA oder EC?

Wer PHP openssl_pkey_new() aufruft, ohne den Typ zu setzen, bekommt RSA. Elliptische Kurven sind die modernere Alternative und in vielen Fällen die bessere Wahl.

Merkmal RSA 3072 EC prime256v1
Schlüsselgröße groß, mehrere Kilobyte als PEM klein, wenige hundert Byte
Erzeugungsdauer spürbar, teils mehrere Sekunden sehr kurz
Verbreitung überall, auch in Altsystemen modern, gelegentlich nicht unterstützt
Direkt verschlüsseln möglich, aber größenbegrenzt nicht vorgesehen, nur signieren
<?php

$ec = openssl_pkey_new([
'private_key_type' => OPENSSL_KEYTYPE_EC,
'curve_name' => 'prime256v1',
]);

openssl_pkey_export($ec, $pem);
echo strlen($pem); // ungefaehr 300 statt mehrerer tausend Zeichen

Für Signaturen an einer Schnittstelle ist EC in aller Regel die bessere Wahl. Wer dagegen mit älteren Partnersystemen sprechen muss, bleibt bei RSA, weil dort die Unterstützung sicher vorhanden ist.

Wenn PHP openssl_pkey_new() false liefert

Der Aufruf schlägt still fehl und gibt false zurück. Die Ursache steht nicht in der PHP-Fehlermeldung, sondern in der Fehlerliste von OpenSSL, und die muss aktiv ausgelesen werden.

<?php

$paar = openssl_pkey_new(['private_key_bits' => 2048]);

if ($paar === false) {
$meldungen = [];
while (($fehler = openssl_error_string()) !== false) {
$meldungen[] = $fehler;
}
echo implode(PHP_EOL, $meldungen);
/* z.B. error:02001002:system library:fopen:No such file or directory */
}

Die Schleife ist wichtig, weil OpenSSL mehrere Meldungen stapelt. Ein einzelner Aufruf holt nur die älteste und lässt die aussagekräftige stehen. Der häufigste Fall auf Entwicklungsrechnern unter Windows ist eine fehlende openssl.cnf. Dann hilft die Option config mit einem eigenen Pfad, oder die Umgebungsvariable OPENSSL_CONF zeigt auf die mitgelieferte Datei.

Was asymmetrische Schlüssel nicht können

Ein Schlüssel aus PHP openssl_pkey_new() verschlüsselt bei RSA nur wenig Daten auf einmal, bei 2048 Bit gut 200 Byte. Eine PDF-Datei damit direkt zu verschlüsseln, funktioniert nicht. In der Praxis wird deshalb ein zufälliger symmetrischer Schlüssel erzeugt, die Nutzdaten damit verschlüsselt und nur dieser kurze Schlüssel asymmetrisch geschützt. Wie die symmetrische Hälfte aussieht, zeigt das Tutorial zu PHP openssl_encrypt() und dem sicheren Verschlüsseln von Daten. Wer wissen will, was in einem fertigen Zertifikat steht, findet das im Tutorial zu PHP openssl_x509_parse().

<?php

/* Hybridverfahren: symmetrisch fuer die Daten, asymmetrisch fuer den Schluessel */
$sitzungsschluessel = random_bytes(32);
$iv = random_bytes(openssl_cipher_iv_length('aes-256-cbc'));

$chiffrat = openssl_encrypt($grosseDaten, 'aes-256-cbc',
$sitzungsschluessel, OPENSSL_RAW_DATA, $iv);

/* nur die 32 Byte gehen asymmetrisch an den Empfaenger */
openssl_public_encrypt($sitzungsschluessel, $geschuetzt, $oeffentlichPem);

$paket = base64_encode($iv . $geschuetzt . $chiffrat);

Nur der kurze Sitzungsschlüssel wird asymmetrisch geschützt, die eigentlichen Nutzdaten laufen über das schnellere symmetrische Verfahren. So arbeiten auch TLS und verschlüsselte E-Mail.

Ebenso wenig gehören Anmeldedaten in dieses Verfahren. Passwörter werden nicht verschlüsselt, sondern gehasht, und dafür ist PHP password_hash() zuständig. Wer beides vermischt, baut sich eine Schwachstelle, die sich später nur schwer wieder ausbauen lässt.

Der folgende Ablauf fasst zusammen, welcher Aufruf welchen Teil liefert und wo die beiden Wege wieder zusammenkommen.

flowchart TD
  A[openssl_pkey_new] --> B[Schluesselobjekt]
  B --> C[openssl_pkey_export]
  B --> D[openssl_pkey_get_details]
  C --> E[privat.pem mit Passphrase]
  D --> F[oeffentlich.pem]
  E --> G[openssl_sign]
  F --> H[openssl_verify]
  G --> H

Der private Zweig bleibt im eigenen System, der öffentliche geht zur Gegenstelle. Erst bei der Prüfung treffen sich beide wieder.

Ein vollständiges Beispiel für signierte Webhooks

Damit steht alles zusammen, was für den Fall aus der Einleitung nötig ist. Der Aufruf von PHP openssl_pkey_new() passiert genau einmal, alles Weitere arbeitet mit den beiden Dateien. Der Sender hängt die Signatur als Header an, der Empfänger prüft sie gegen den öffentlichen Schlüssel.

<?php

/* einmalig: Paar erzeugen und ablegen */
function schluesselErzeugen(string $verzeichnis, string $passphrase): void
{
$paar = openssl_pkey_new([
'private_key_type' => OPENSSL_KEYTYPE_EC,
'curve_name' => 'prime256v1',
]);
if ($paar === false) {
throw new RuntimeException('Schluessel nicht erzeugbar');
}
openssl_pkey_export($paar, $privat, $passphrase);
$oeffentlich = openssl_pkey_get_details($paar)['key'];

file_put_contents($verzeichnis . '/privat.pem', $privat);
file_put_contents($verzeichnis . '/oeffentlich.pem', $oeffentlich);
chmod($verzeichnis . '/privat.pem', 0600);
}

/* bei jedem Versand */
function signatur(string $rumpf, string $pfad, string $passphrase): string
{
$key = openssl_pkey_get_private(file_get_contents($pfad), $passphrase);
if ($key === false) {
throw new RuntimeException('Privater Schluessel nicht ladbar');
}
openssl_sign($rumpf, $roh, $key, OPENSSL_ALGO_SHA256);

return base64_encode($roh);
}

Die Zeile mit chmod() ist kein Beiwerk. Auf einem Server mit mehreren Konten entscheidet sie darüber, ob der private Schlüssel für andere lesbar ist. Wer HTTP-Header für die Übertragung braucht, findet die Grundlagen im Tutorial zu HTTP-Headern mit PHP.

Fazit

PHP openssl_pkey_new() erzeugt in einem Aufruf ein Paar, dessen Hälften getrennte Wege gehen. Der private Teil bleibt mit Passphrase und engen Dateirechten im eigenen System, der öffentliche darf frei weitergegeben werden. Erst zusammen mit openssl_sign() und openssl_verify() entsteht daraus ein nutzbares Verfahren, und die strikte Prüfung auf === 1 entscheidet darüber, ob es auch hält.

Wer heute neu anfängt, wählt EC statt RSA, setzt die Optionen ausdrücklich und liest im Fehlerfall openssl_error_string() in einer Schleife aus. Damit sind die drei Stellen abgedeckt, an denen die Arbeit mit PHP openssl_pkey_new() üblicherweise stecken bleibt.

 


 

Kommentare (0)

Noch keine Kommentare. Sei der Erste!

Melde dich an, um einen Kommentar zu schreiben.
Bücherregal mit drei Büchern: 'PHP 4 - Grundlagen und Profiwissen' von Hanser Verlag, 'Webdesign in a Nutshell' von O'Reilly Verlag, und 'Webgestaltung' von Galileo Computing.