Post-Quanten-Kryptografie in Delphi

· Komponenten
Post-Quanten-Kryptografie in Delphi

Es lohnt sich, bei den Daten, die Ihre Anwendung heute verarbeitet, eine Frage zu stellen: Wie lange bleiben sie sensibel? Eine Krankenakte, ein Vertrag, eine Gehaltsabrechnung, ein Satz Zugangsdaten. Wenn die Antwort mehr als ein paar Jahre lautet, dann ist die eigentliche Bedrohung nicht ein Angreifer, der Ihre Verschlüsselung heute bricht. Es ist ein Angreifer, der den Datenverkehr schon jetzt aufzeichnet, speichert und später entschlüsselt, sobald die Werkzeuge dafür so weit sind. Diese Praxis hat einen Namen, harvest now, decrypt later (jetzt sammeln, später entschlüsseln), und dafür muss heute niemand einen Quantencomputer bauen. Es reicht, wenn jemand glaubt, dass es irgendwann einer tun wird.

Deshalb haben sich die Standards zuerst bewegt. NIST hat die Algorithmen 2024 veröffentlicht, Browser und CDNs haben 2025 den hybriden Schlüsselaustausch aktiviert, und die Fristen für die Ablösung des Schlüsselaustauschs mit RSA und elliptischen Kurven stehen inzwischen in den öffentlichen Richtlinien. sgcWebSockets 2026.10 bringt das gesamte Paket nach Delphi, geschrieben in Object Pascal innerhalb des sgcCrypto-Pakets, ohne externe Bibliothek, die installiert oder mit ausgeliefert werden müsste.

Drei Algorithmen, ein Paket

Die drei NIST-Standards erfüllen unterschiedliche Aufgaben, und normalerweise werden Sie die ersten beiden verwenden:

Einen Schlüssel mit ML-KEM aushandeln

Ein Schlüsselkapselungsmechanismus ist einfacher zu benutzen als Diffie-Hellman. Der Absender nimmt den öffentlichen Schlüssel des Empfängers und erzeugt zwei Dinge: ein frisches gemeinsames Geheimnis und einen Chiffretext, der es überträgt. Der Empfänger verwandelt den Chiffretext wieder in dasselbe Geheimnis.

uses
  sgcCrypto_MLKEM;

var
  oPublicKey, oPrivateKey, oCiphertext, oSecret, oSame: TBytes;
begin
  sgcMLKEM_GenerateKeyPair(mlkem768, oPublicKey, oPrivateKey);

  // the sender, who only ever sees the public key
  sgcMLKEM_Encapsulate(mlkem768, oPublicKey, oCiphertext, oSecret);

  // the recipient, who holds the private key, gets the same 32 bytes
  oSame := sgcMLKEM_Decapsulate(mlkem768, oPrivateKey, oCiphertext);
end;

Die Parametersätze sind mlkem512, mlkem768 und mlkem1024. Die von FIPS 203 geforderten Eingabeprüfungen laufen vor allem anderen, sodass ein fehlerhaft geformter öffentlicher Schlüssel eine Exception auslöst, statt still ein Geheimnis zu erzeugen, und ein Chiffretext, der sich nicht entschlüsseln lässt, ein pseudozufälliges Geheimnis liefert statt eines Fehlers, was genau die implizite Zurückweisung ist, die der Standard verlangt.

X-Wing, wenn Sie sich nicht festlegen wollen

Einen neuen Algorithmus einzusetzen bedeutet, ihm zu vertrauen. Hybride Konstruktionen nehmen Ihnen diese Entscheidung ab: Sie kombinieren einen klassischen Algorithmus mit einem post-quantenfähigen, sodass das Ergebnis sicher bleibt, solange eine der beiden Hälften hält. X-Wing kombiniert X25519 mit ML-KEM-768 und stellt ein einziges Schlüsselpaar bereit.

uses
  sgcCrypto_MLKEM_Hybrid;

var
  oPublicKey, oPrivateKey, oCiphertext, oSecret: TBytes;
begin
  sgcXWing_GenerateKeyPair(oPublicKey, oPrivateKey);
  sgcXWing_Encapsulate(oPublicKey, oCiphertext, oSecret);
  oSecret := sgcXWing_Decapsulate(oPrivateKey, oCiphertext);
end;

Signieren mit ML-DSA

Das Signieren nimmt den Parametersatz, den privaten Schlüssel, die Nachricht und eine Kontextzeichenfolge entgegen, die normalerweise leer ist:

uses
  sgcCrypto_MLDSA;

var
  oSeed, oPublicKey, oPrivateKey, oSignature: TBytes;
begin
  sgcMLDSA_GenerateKeyPairAndSeed(mldsa65, oSeed, oPublicKey, oPrivateKey);

  oSignature := sgcMLDSA_Sign(mldsa65, oPrivateKey, aMessage, nil);

  if sgcMLDSA_Verify(mldsa65, oPublicKey, aMessage, oSignature, nil) then
    ShowMessage('signature is valid');
end;

Beachten Sie den Seed. Ein privater ML-DSA-Schlüssel lässt sich als die 32 Bytes speichern, aus denen er erzeugt wurde, als der expandierte Schlüssel, oder als beides, und die Bibliothek liest und schreibt alle drei Formen. Der Seed ist die Form, die Sie in einer Konfigurationsdatei wollen: Er ist klein, und der expandierte Schlüssel wird deterministisch aus ihm abgeleitet.

Schlüssel, die reisen

Ein Algorithmus, mit dem niemand Schlüssel austauschen kann, nützt wenig, deshalb folgen die Kodierungen den veröffentlichten Profilen: SubjectPublicKeyInfo und PKCS#8, in DER oder PEM, nach RFC 9935 für ML-KEM, RFC 9881 für ML-DSA und RFC 9909 für SLH-DSA.

var
  vPublicPEM, vPrivatePEM: string;
begin
  vPublicPEM := sgcMLDSA_ExportPublicKeyPEM(mldsa65, oPublicKey);
  vPrivatePEM := sgcMLDSA_ExportPrivateKeyPEM(mldsa65, oSeed, nil);
end;

Das sind gewöhnliche BEGIN PUBLIC KEY- und BEGIN PRIVATE KEY-Blöcke, die andere Implementierungen lesen können. Ein privater Schlüssel wird beim Import geprüft: Die öffentliche Hälfte wird erneut abgeleitet und verglichen, sodass ein Schlüssel, der nicht zu seinem Zertifikat gehört, sofort zurückgewiesen wird, statt Signaturen zu erzeugen, die niemand verifizieren kann.

Zertifikate und eine post-quantenfähige CA

Zertifikate und Zertifikatsanfragen können post-quantenfähige Schlüssel tragen, und auch eine Zertifizierungsstelle kann einen solchen besitzen, sodass eine ganze Kette post-quantenfähig sein kann:

var
  vKey: TsgcX509SignKey;
  vOptions: TsgcX509Options;
  oCertDER: TBytes;
begin
  vKey := sgcX509_MLDSAKey(mldsa65, oPrivateKey, oPublicKey);
  vOptions.Subject.CommonName := 'My Post-Quantum CA';
  vOptions.KeyUsage := [kuKeyCertSign, kuCRLSign];
  oCertDER := sgcX509_CreateSelfSignedEx(vKey, vOptions);
end;

Die Regeln zur Schlüsselverwendung aus den Profilen werden durchgesetzt, bevor irgendetwas signiert wird, sodass ein post-quantenfähiger Schlüssel, dem eine Schlüsselverschlüsselung abverlangt wird, eine Exception auslöst, statt ein Zertifikat zu erzeugen, das sein eigenes Profil verletzt. Die Kettenvalidierung setzt inzwischen auch die Pfadlänge und die Namensbeschränkungen durch, die die ausstellenden Zertifikate deklarieren.

Wo es bereits eingebunden ist

An zwei Stellen in der Bibliothek wird das alles bereits für Sie erledigt, ohne dass Sie Kryptografie schreiben müssen:

Woher wir wissen, dass es korrekt ist

Die Implementierung eines Standards ist nur eine Behauptung, bis etwas sie überprüft. Alle drei Algorithmen werden gegen die Known-Answer-Vektoren von NIST validiert, und diese Vektoren werden mit der Bibliothek als lauffähiges Testset ausgeliefert, nicht als Satz in einem Datenblatt. Der ML-DSA-Signierer ist die deterministische Variante, sodass derselbe Schlüssel und dieselbe Nachricht immer dieselbe Signatur ergeben, was die in den RFCs veröffentlichten Beispiele byte-für-byte reproduzierbar macht.

Was Sie verwenden sollten

Für den Schlüsselaustausch verwenden Sie ein Hybrid: X-Wing allein, oder die hybriden TLS-Gruppen, wenn der Datenverkehr TLS ist. Sie geben so gut wie nichts auf und sind abgesichert, welche Annahme sich auch als falsch erweisen sollte. Für Signaturen ist ML-DSA-65 die sinnvolle Standardwahl, mit ML-DSA-44, wenn die Größe zählt, und ML-DSA-87, wenn nicht. Greifen Sie zu SLH-DSA, wenn die Signatur viele Jahre später von etwas verifiziert wird, das Sie nicht aktualisieren können, etwa Firmware, und die Signaturgröße vertretbar ist.

Aktualisieren

All das steckt im sgcCrypto-Paket und fügt keine Abhängigkeit hinzu: kein OpenSSL, keine DLL, nichts, was auf dem Rechner installiert werden müsste, der Ihre Anwendung ausführt. Bestehender Code bleibt unangetastet, bis Sie ihn aufrufen.

Weiterlesen

Ansehen

Dazu gibt es ein kurzes Video auf dem eSeGeCe Kanal.

Fragen, Feedback oder Hilfe bei der Migration? Nehmen Sie Kontakt auf — Sie erhalten eine Antwort von den Leuten, die den Code geschrieben haben.