Ein JWT ist eine kleine Sache mit großer Reichweite. Er ist der Identitätsnachweis zwischen Ihren Diensten, und der Algorithmus, der ihn signiert, ist meist der Teil eines Systems, der sich am langsamsten ändern lässt, weil jeder Aussteller und jeder Prüfer gleichzeitig umstellen muss. Deshalb lohnt es sich, zu wissen, dass diese Option existiert, bevor man sie braucht.
sgcWebSockets 2026.10 fügt dem JWT-Client und -Server die post-quantenfähigen JWT-Algorithmen aus RFC 9964 hinzu: ML-DSA-44, ML-DSA-65 und ML-DSA-87. Sie stehen neben HS, RS und ES als drei weitere Werte derselben Eigenschaft, und alles andere an Ihrem Token bleibt, wie es war.
Ein Token ausstellen
Wählen Sie den Algorithmus im Header, geben Sie der Komponente einen privaten Schlüssel und signieren Sie:
oJWT := TsgcHTTP_JWT_Client.Create(nil);
oJWT.JWTOptions.Header.alg := jwtMLDSA65;
oJWT.JWTOptions.Algorithms.MLDSA.PrivateKey.Text := vPrivatePEM;
oJWT.JWTOptions.Payload.iss := 'my-service';
oJWT.JWTOptions.Payload.sub := 'user-1';
vToken := oJWT.Sign;
Der private Schlüssel ist ein gewöhnliches PKCS#8-PEM. Alle drei Formen, die RFC 9881 definiert, werden gelesen, sodass ein Schlüssel, der als 32-Byte-Seed, als expandierter Schlüssel oder als beides gespeichert ist, in jedem Fall funktioniert.
Einen validieren
Die Serverseite hat dieselbe Form, mit dem öffentlichen Schlüssel und der aktivierten Familie:
oServer := TsgcHTTP_JWT_Server.Create(nil);
oServer.JWTOptions.Algorithms.MLDSA.Enabled := True;
oServer.JWTOptions.Algorithms.MLDSA.PublicKey.Text := vPublicPEM;
if oServer.Validate(vToken, vHeader, vPayload, vError) then
ShowMessage(vPayload);
Jede Familie wird einzeln freigeschaltet, sodass eine Installation, die ausschließlich ML-DSA-Tokens ausstellt, die anderen abschalten kann, und ein Token, das mit einem anderen Wert für alg ankommt, wird zurückgewiesen statt geprüft.
Der Parametersatz muss passen
Es gibt eine Regel in RFC 9964, die es wert ist, laut ausgesprochen zu werden, weil sie der Fehler ist, der sonst unbemerkt bliebe: Der Schlüssel muss zu dem Parametersatz gehören, den alg benennt. Ein Schlüssel, der nicht passt, wird zurückgewiesen. Signieren mit dem falschen löst eine Exception aus, und Validieren mit dem falschen liefert false statt eine Exception auszulösen, sodass ein Angreifer aus diesem Unterschied nichts lernen kann.
Schlüssel als JSON Web Keys
RFC 9964 registriert für diese Algorithmen einen Schlüsseltyp, AKP, und die Bibliothek liest und schreibt ihn, was genau das ist, was Sie wollen, wenn Schlüssel an einem JWKS-Endpunkt veröffentlicht werden, statt in einer Datei abgelegt zu werden:
uses
sgcHTTP_JWT_MLDSA;
var
vJWK, vPublicPEM, vPrivatePEM: string;
begin
// {"kty":"AKP","alg":"ML-DSA-65","pub":"...","priv":"..."}
vJWK := sgcMLDSA_ExportPrivateJWK(mldsa65, oSeed);
// and back again, as the PEM the components read
sgcMLDSA_ImportJWKAsPEM(vJWK, vPublicPEM, vPrivatePEM);
end;
Das private Member ist der 32-Byte-Seed, wie ihn das RFC definiert, und der Leser ist dabei strikt: Die Member müssen kanonisches base64url mit genau der richtigen Länge sein, alg ist erforderlich, und wenn ein privater Schlüssel vorhanden ist, muss er mit dem öffentlichen im selben JWK übereinstimmen. Ein JWK, das eine dieser Bedingungen verletzt, wird zurückgewiesen, statt halb geladen zu werden.
Interoperabilität, nachgewiesen
Ein Signaturformat ist nur nützlich, wenn jemand anderes dieselben Bytes erzeugt. Zwei Prüfungen werden als Tests ausgeliefert, nicht als bloße Behauptungen:
- Die veröffentlichten Beispiele von RFC 9964 werden byte-für-byte reproduziert. Der Signierer ist die deterministische Variante, sodass derselbe Schlüssel und derselbe Input immer dieselbe Signatur ergeben, und der Anhang des RFC wird zu etwas, worauf man eine Assertion schreiben kann.
- Tokens, die von einer unabhängigen Implementierung erzeugt wurden, in diesem Fall der Python-Bibliothek dilithium-py, werden hier validiert, und die von Hand nach RFC 9881 geschriebenen Schlüsselcontainer bleiben beim Hin- und Zurückkonvertieren unverändert.
Da wir schon dabei waren: ein binäres HMAC-Secret
Eine damit nicht zusammenhängende Einschränkung ist im selben Zug verschwunden. Die HS-Algorithmen nahmen ihren Schlüssel als Text entgegen, und ein echter HMAC-Schlüssel besteht aus zufälligen Bytes, von denen die meisten überhaupt kein gültiger Text sind. Es gibt jetzt eine zweite Eigenschaft, die den Schlüssel als base64url entgegennimmt, dieselbe Kodierung, die das k-Member eines oct-JWK verwendet:
oJWT.JWTOptions.Algorithms.HS.SecretBase64URL := 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ...';
Wenn sie gesetzt ist, hat sie Vorrang vor dem Text-Secret, sowohl auf dem Client als auch auf dem Server.
Von jemand anderem geschriebene Claims
Noch eine Korrektur, die zu kennen sich lohnt, wenn Sie Tokens von anderen Ausstellern akzeptieren. Ein JSON-String kann als rohes UTF-8 oder mit jedem Nicht-ASCII-Zeichen als Escape-Sequenz geschrieben werden, und verschiedene Bibliotheken entscheiden sich unterschiedlich. Beide Formen führen jetzt zum selben Text, sodass ein Name mit einem Akzent korrekt gelesen wird, egal welcher Aussteller ihn erzeugt hat, und ein mit Escape-Sequenzen geschriebener alg-Header als der Algorithmus erkannt wird, den er bezeichnet.
Aktualisieren
Die drei Algorithmen werden am Ende der bestehenden Liste ergänzt, sodass ein gespeicherter Ordinalwert seine Bedeutung behält und sich an dem, was Sie bereits tun, nichts ändert. Signieren mit ML-DSA benötigt das sgcCrypto-Paket, und ein Build ohne dieses Paket weist solche Tokens mit einer klaren Meldung zurück, statt auf undurchsichtige Weise zu scheitern.
Weiterlesen
- Post-Quanten-Kryptografie in Delphi
- Delphi TLS 1.3 ohne OpenSSL-DLLs
- JWT Delphi Client und JWT Delphi Server, die Grundlagen
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.
