Die erste hybride Zertifikatskette
Sie stellen für die fiktive Muster GmbH ein hybrides Stammzertifikat und damit ein Serverzertifikat für
www.example.com aus und prüfen die Kette – einmal so, wie jede bestehende Software sie prüft, und einmal
mit dem Post-Quanten-Anteil. „Hybrid“ heißt: Jedes Zertifikat trägt seine gewohnte RSA-Signatur und
zusätzlich eine ML-DSA-Signatur. Software ohne PQ-Kenntnis sieht ein gewöhnliches RSA-Zertifikat.
Voraussetzungen
- .NET 8 SDK oder höher zum Ausführen des Beispiels, .NET 10 SDK zum Übersetzen der Bibliothek.
- OpenSSL ab 3.5 für die Prozessarchitektur, siehe Voraussetzungen.
- Optional für die Gegenprobe in Schritt 6: ein beliebiges OpenSSL-Kommandozeilenwerkzeug ab 3.0.
Schritte
Schritt 1: Projekt anlegen und die Bibliothek einbinden
dotnet new console -n MusterPki -f net8.0 cd MusterPki dotnet add reference ..\sys-sec-crypto\WKL.System.Security.Cryptography.Extensions.csproj
Passen Sie den Pfad an den Ort an, an dem der Quelltext der Bibliothek liegt.
Schritt 2: OpenSSL einbinden und den hybriden Betrieb einschalten
Ersetzen Sie den Inhalt von Program.cs durch:
using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using WKL.System.Security.Cryptography; using WKL.System.Security.Cryptography.X509Certificates; PostQuantumConfig.OpenSslPath = @"C:\OpenSSL\bin"; PostQuantumConfig.EnablePostQuantum(CryptoMode.Hybrid); Console.WriteLine(PostQuantumConfig.GetOpenSslVersion());
OpenSslPath ist das Verzeichnis mit libcrypto. EnablePostQuantum lädt OpenSSL und wirft eine
CryptographicException, wenn es fehlt oder ML-DSA und ML-KEM nicht anbietet. Die Bibliothek arbeitet also
nie unbemerkt ohne PQ-Anteil weiter.
Kontrolle: dotnet run gibt die OpenSSL-Version aus, etwa OpenSSL 3.6.1 27 Jan 2026.
Schritt 3: Das hybride Stammzertifikat ausstellen
Hängen Sie an:
using var rootKey = RSA.Create(3072); var rootRequest = new CertificateRequest( "CN=Muster GmbH Root CA", rootKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); rootRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true)); rootRequest.CertificateExtensions.Add( new X509KeyUsageExtension(X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign, true)); using var root = rootRequest.CreateSelfSigned( DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(5), CryptoMode.Hybrid); Console.WriteLine(root.GetSignatureAlgorithmDescription());
Das ist der Aufruf von .NET mit einem zusätzlichen Argument. Ohne CryptoMode.Hybrid entstünde ein
gewöhnliches RSA-Zertifikat. Die Bibliothek erzeugt für das Zertifikat ein ML-DSA-65-Schlüsselpaar und hält
den privaten Schlüssel für die Laufzeit des Prozesses bereit.
Kontrolle: Die Ausgabe lautet sha256RSA + ML-DSA-65 (Hybrid).
Schritt 4: Das Serverzertifikat ausstellen
using var leafKey = RSA.Create(2048); var leafRequest = new CertificateRequest( "CN=www.example.com", leafKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); using var leaf = leafRequest.Create( root, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(1), RandomNumberGenerator.GetBytes(16), CryptoMode.Hybrid); using var server = leaf.CopyWithPrivateKey(leafKey); Console.WriteLine(server.HasPqSignature());
Das Stammzertifikat signiert mit seinem RSA-Schlüssel und mit seinem ML-DSA-Schlüssel aus Schritt 3. Fehlt
dieser Schlüssel im Prozess – etwa weil das Stammzertifikat als PEM-Datei statt als PFX geladen wurde –,
scheitert der Aufruf mit einer CryptographicException, die den Aussteller nennt.
Kontrolle: Die Ausgabe lautet True.
Schritt 5: Die Kette mit Post-Quanten-Anteil prüfen
using var chain = new X509Chain(); chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; chain.ChainPolicy.CustomTrustStore.Add(root); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; bool valid = chain.Build(server, CryptoMode.Hybrid); Console.WriteLine($"Kette gültig: {valid}"); foreach (var element in chain.GetValidationReport().Elements) { Console.WriteLine($"{element.Subject}: {element.PqSignatureStatus}"); } File.WriteAllText("root.pem", root.ExportCertificatePem()); File.WriteAllText("server.pem", server.ExportCertificatePem());
Build mit CryptoMode.Hybrid führt zuerst die gewohnte Prüfung von .NET durch und prüft danach jede
ML-DSA-Signatur der Kette. Eine ungültige Signatur ergibt false, wie bei .NET; den klassischen Grund nennt
chain.ChainStatus, das PQ-Ergebnis je Zertifikat GetValidationReport().
Kontrolle: Die Ausgabe lautet:
Kette gültig: True CN=www.example.com: Valid CN=Muster GmbH Root CA: Valid
Schritt 6: Gegenprobe mit einem klassischen Prüfer
openssl verify -CAfile root.pem server.pem
OpenSSL prüft die RSA-Signaturen und übergeht die nicht kritischen Erweiterungen mit dem PQ-Anteil.
Kontrolle: Die Ausgabe lautet server.pem: OK.
Ergebnis
Sie haben zwei Zertifikate, die jede bestehende Software als RSA-Zertifikate annimmt und deren ML-DSA-Signaturen die Bibliothek zusätzlich prüft. Die privaten ML-DSA-Schlüssel leben nur im laufenden Prozess. Um sie dauerhaft mitzunehmen, exportieren Sie das Zertifikat als PFX.