Een JWT is een klein ding met een lange reikwijdte. Het is het identiteitsbewijs tussen uw services, en het algoritme dat het ondertekent is doorgaans het onderdeel van een systeem dat het traagst te wijzigen is, omdat elke uitgever en elke verifiëerder tegelijk moeten meebewegen. Daarom is het de moeite waard om te weten dat de optie bestaat voordat u ze nodig hebt.
sgcWebSockets 2026.10 voegt de post-kwantum JWT-algoritmen van de RFC 9964 toe aan de JWT-client en -server: ML-DSA-44, ML-DSA-65 en ML-DSA-87. Ze staan naast HS, RS en ES als drie extra waarden van dezelfde eigenschap, en al het andere aan uw token blijft ongewijzigd.
Een token uitgeven
Kies het algoritme in de header, geef het component een private sleutel en onderteken:
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;
De private sleutel is een gewone PKCS#8-PEM. Alle drie de vormen die RFC 9881 definieert worden gelezen, zodat een sleutel die is opgeslagen als de seed van 32 bytes, als de uitgebreide sleutel, of als beide, in alle gevallen werkt.
Een token valideren
De serverkant heeft dezelfde vorm, met de publieke sleutel en de familie ingeschakeld:
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);
Elke familie wordt afzonderlijk in- of uitgeschakeld, zodat een implementatie die alleen ML-DSA-tokens uitgeeft de andere kan uitschakelen, en een token dat binnenkomt met alg ingesteld op iets anders wordt geweigerd in plaats van gecontroleerd.
De parameterset moet overeenkomen
Er is één regel in RFC 9964 die het waard is expliciet te vermelden, omdat het de fout is die anders onopgemerkt zou blijven: de sleutel moet behoren tot de parameterset die alg noemt. Een sleutel die niet overeenkomt, wordt geweigerd. Ondertekenen met de verkeerde veroorzaakt een uitzondering, en valideren met de verkeerde geeft onwaar terug in plaats van een uitzondering te veroorzaken, zodat een aanvaller dat verschil niet kan gebruiken om iets te weten te komen.
Sleutels als JSON Web Keys
RFC 9964 registreert een sleuteltype voor deze algoritmen, AKP, en de bibliotheek leest en schrijft het, wat precies is wat u wilt wanneer sleutels worden gepubliceerd op een JWKS-eindpunt in plaats van in een bestand te worden gezet:
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;
Het private lid is de seed van 32 bytes, wat de RFC voorschrijft, en de lezer is daar strikt in: de leden moeten canonieke base64url zijn met precies de juiste lengte, alg is verplicht, en wanneer er een private sleutel aanwezig is, moet die overeenkomen met de publieke sleutel in hetzelfde JWK. Een JWK die op een van deze punten faalt, wordt afgewezen in plaats van half geladen.
Interoperabiliteit, aangetoond
Een handtekeningformaat is alleen nuttig als iemand anders dezelfde bytes produceert. Twee controles worden meegeleverd als tests in plaats van als beweringen:
- De gepubliceerde voorbeelden van RFC 9964 worden byte voor byte gereproduceerd. De ondertekenaar is de deterministische variant, zodat dezelfde sleutel en dezelfde invoer altijd dezelfde handtekening opleveren, waardoor de bijlage van de RFC iets wordt waarop u kunt testen.
- Tokens geproduceerd door een onafhankelijke implementatie, in dit geval de Python-bibliotheek dilithium-py, worden hier gevalideerd, en de sleutelcontainers die met de hand zijn geschreven op basis van RFC 9881 doorstaan de heen-en-terugreis ongewijzigd.
Terwijl we er toch mee bezig waren: een binair HMAC-geheim
Een ongerelateerde beperking verdween in dezelfde slag. De HS-algoritmen namen hun sleutel als tekst, terwijl een echte HMAC-sleutel uit willekeurige bytes bestaat, waarvan de meeste helemaal geen geldige tekst zijn. Er is nu een tweede eigenschap die de sleutel als base64url accepteert, dezelfde codering die het lid k van een oct-JWK gebruikt:
oJWT.JWTOptions.Algorithms.HS.SecretBase64URL := 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ...';
Wanneer deze is ingesteld, krijgt ze voorrang boven het tekstgeheim, zowel op de client als op de server.
Claims geschreven door iemand anders
Nog een correctie die het waard is te kennen als u tokens van andere uitgevers accepteert. Een JSON-string kan worden geschreven als ruwe UTF-8 of met elk niet-ASCII-teken ge-escaped, en verschillende bibliotheken kiezen daar anders in. Beide vormen leiden nu tot dezelfde tekst, zodat een naam met een accent correct wordt gelezen ongeacht welke uitgever hem heeft geproduceerd, en een alg-header die met escapes is geschreven wordt herkend als het algoritme dat hij aanduidt.
Upgraden
De drie algoritmen worden toegevoegd aan het einde van de bestaande lijst, zodat een opgeslagen ordinaal zijn betekenis behoudt en niets van wat u al doet verandert. Ondertekenen met ML-DSA vereist het sgcCrypto-pakket, en een build zonder dat pakket weigert die tokens met een duidelijk bericht in plaats van op een onduidelijke manier te falen.
Lees ook
- Post-kwantumcryptografie in Delphi
- Delphi TLS 1.3 zonder OpenSSL-DLL's
- JWT Delphi-client en JWT Delphi-server, de basis
Bekijk het
Er staat een korte video hierover op het eSeGeCe-kanaal.
Vragen, feedback of hulp bij migratie? Neem contact op — u krijgt een antwoord van de mensen die de code hebben geschreven.
