Mensagem de erro
signature verification failed
jose (JavaScript) — JWSSignatureVerificationFailed, code ERR_JWS_SIGNATURE_VERIFICATION_FAILED
O que significa
O jose analisou o JWS com sucesso e executou a verificação criptográfica, mas a assinatura não confere com a chave que ele selecionou. Estrutura, algoritmo e tipo de chave eram todos aceitáveis — a matemática simplesmente não fechou, o que aponta para a chave ou para bytes assinados que foram alterados.
Por que acontece
Chave errada para este token
comumO verificador tem uma chave diferente da que o emissor usou para assinar: um JWKS rotacionado em que o kid antigo não existe mais, uma chave fixa no código que ficou desatualizada ou uma confusão entre ambientes (verificador de homologação, token de produção).
Os bytes assinados mudaram entre o signatário e o verificador
comumO JWS assina exatamente o texto base64url do cabeçalho e do payload, unidos por um ponto. Qualquer coisa que recodifique esses segmentos — um gateway que normaliza o padding base64, uma reserialização do JSON, caracteres removidos no transporte — quebra a verificação, mesmo que o payload decodificado pareça idêntico.
Um signatário ES256 implementado à mão produziu DER
ocasionalO crypto.sign() do Node emite ASN.1 DER para ECDSA por padrão, mas a RFC 7518 exige que as assinaturas JWS ES256 sejam r‖s bruto (exatamente 64 bytes). Um token cujo terceiro segmento decodifica para ~70–72 bytes começando com 0x30 foi assinado no formato errado.
Como corrigir
- 1.
Resolver as chaves pelo kid a partir do JWKS do emissor
Deixe o jose escolher a chave certa para cada token em vez de fixar uma manualmente; o createRemoteJWKSet guarda as chaves em cache e as atualiza entre rotações.
js import { createRemoteJWKSet, jwtVerify } from "jose"; const jwks = createRemoteJWKSet(new URL("https://issuer.example/.well-known/jwks.json")); const { payload } = await jwtVerify(token, jwks, { algorithms: ["ES256"], issuer: "https://issuer.example", }); - 2.
Ao assinar com node:crypto, emitir r‖s bruto
Em signatários personalizados, solicite a saída IEEE P1363 para que a assinatura tenha o formato de 64 bytes que o JWS exige — ou verifique o que seu signatário produziu conferindo o comprimento do terceiro segmento.
js import { sign } from "node:crypto"; const sig = sign("sha256", Buffer.from(signingInput), { key: privateKey, dsaEncoding: "ieee-p1363", // raw r||s, 64 bytes for P-256 }); - 3.
Conferir o formato do segmento de assinatura
Decodifique o terceiro segmento do token: 64 bytes significam r‖s bruto (correto para ES256); cerca de 70 bytes começando com 0x30 significam DER. O conversor DER ⇄ bruto abaixo mostra os valores de r e s e converte entre os dois formatos.