Um JWT é algo pequeno com um alcance enorme. É a prova de identidade entre seus serviços, e o algoritmo que o assina costuma ser a parte de um sistema mais lenta de mudar, porque cada emissor e cada verificador precisam mudar ao mesmo tempo. Por isso vale a pena saber que a opção existe antes de você precisar dela.
O sgcWebSockets 2026.10 adiciona ao cliente e ao servidor JWT os algoritmos JWT pós-quânticos da RFC 9964: ML-DSA-44, ML-DSA-65 e ML-DSA-87. Eles ficam ao lado de HS, RS e ES como mais três valores da mesma propriedade, e tudo o mais no seu token permanece como estava.
Emitindo um token
Escolha o algoritmo no cabeçalho, forneça ao componente uma chave privada e assine:
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;
A chave privada é um PEM PKCS#8 comum. As três formas definidas pela RFC 9881 são lidas, de modo que uma chave armazenada como a semente de 32 bytes, como a chave expandida, ou como ambas, funciona em qualquer um dos casos.
Validando um token
O lado do servidor tem o mesmo formato, com a chave pública e a família ativadas:
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);
Cada família é habilitada de forma independente, de modo que uma implantação que só emite tokens ML-DSA pode desativar as outras, e um token que chega com alg definido para outra coisa é recusado em vez de verificado.
O conjunto de parâmetros precisa corresponder
Há uma regra na RFC 9964 que vale a pena declarar explicitamente, porque é o erro que passaria despercebido: a chave precisa pertencer ao conjunto de parâmetros que o alg indica. Uma chave que não corresponde é recusada. Assinar com a errada gera uma exceção, e validar com a errada retorna falso em vez de gerar exceção, de modo que um atacante não pode usar essa diferença para aprender nada.
Chaves como JSON Web Keys
A RFC 9964 registra um tipo de chave para esses algoritmos, AKP, e a biblioteca lê e grava esse tipo, o que é exatamente o que você quer quando as chaves são publicadas em um endpoint JWKS em vez de guardadas em um arquivo:
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;
O membro privado é a semente de 32 bytes, que é o que a RFC define, e o leitor é rigoroso quanto a isso: os membros devem ser base64url canônico com exatamente o comprimento correto, alg é obrigatório, e quando uma chave privada está presente ela precisa concordar com a chave pública no mesmo JWK. Um JWK que falha em qualquer um desses pontos é rejeitado em vez de carregado pela metade.
Interoperabilidade demonstrada
Um formato de assinatura só é útil se outra pessoa produzir os mesmos bytes. Duas verificações são entregues como testes, e não apenas como afirmações:
- Os exemplos publicados na RFC 9964 são reproduzidos byte a byte. O assinador é a variante determinística, de modo que a mesma chave e a mesma entrada sempre produzem a mesma assinatura, o que transforma o apêndice da RFC em algo que se pode verificar por asserção.
- Tokens produzidos por uma implementação independente, neste caso a biblioteca Python dilithium-py, se validam aqui, e os contêineres de chave escritos manualmente a partir da RFC 9881 fazem o ciclo completo sem alterações.
Já que estávamos ali: um segredo HMAC binário
Uma limitação sem relação com o assunto desapareceu na mesma passagem. Os algoritmos HS recebiam a chave como texto, e uma chave HMAC de verdade é composta de bytes aleatórios, a maioria dos quais nem sequer é texto válido. Agora existe uma segunda propriedade que recebe a chave como base64url, a mesma codificação usada pelo membro k de um JWK do tipo oct:
oJWT.JWTOptions.Algorithms.HS.SecretBase64URL := 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ...';
Quando definida, ela tem precedência sobre o segredo em texto, tanto no cliente quanto no servidor.
Claims escritos por outra pessoa
Mais uma correção que vale a pena conhecer se você aceita tokens de outros emissores. Uma string JSON pode ser escrita como UTF-8 puro ou com cada caractere não ASCII escapado, e bibliotecas diferentes escolhem de formas diferentes. As duas formas agora resultam no mesmo texto, de modo que um nome com acento é lido corretamente qualquer que seja o emissor que o produziu, e um cabeçalho alg escrito com escapes é reconhecido como o algoritmo que ele representa.
Atualizando
Os três algoritmos são adições ao final da lista existente, de modo que um ordinal já armazenado mantém seu significado e nada do que você já faz muda. Assinar com ML-DSA exige o pacote sgcCrypto, e uma build sem ele recusa esses tokens com uma mensagem clara em vez de falhar de forma obscura.
Leia também
- Criptografia pós-quântica em Delphi
- Delphi TLS 1.3 sem DLLs OpenSSL
- Cliente JWT Delphi e Servidor JWT Delphi, o básico
Assista
Há um vídeo curto sobre isso no canal eSeGeCe.
Perguntas, feedback ou ajuda com a migração? Entre em contato — você receberá uma resposta das pessoas que escreveram o código.
