Mensagem de erro
token signature is invalid: crypto/ecdsa: verification error
Também aparece como
- token signature is invalid: crypto/rsa: verification error
- token signature is invalid: signature is invalid
- token signature is invalid: ed25519: verification error
- crypto/ecdsa: verification error
O golang-jwt v4 retorna só o texto interno, por exemplo, “crypto/ecdsa: verification error”. Use `errors.Is(err, jwt.ErrTokenSignatureInvalid)` (v5) em vez de comparar strings.
golang-jwt/jwt (Go) — jwt.ErrTokenSignatureInvalid joined with the algorithm's verification error
O que significa
O golang-jwt analisou o token, aceitou o algoritmo dele e obteve uma chave do seu `Keyfunc`, mas a verificação da assinatura com essa chave falhou. Na v5, o erro combina `jwt.ErrTokenSignatureInvalid` (“token signature is invalid”) com o erro do próprio algoritmo: `crypto/ecdsa: verification error` para ES*, `crypto/rsa: verification error` para RS*/PS*, `signature is invalid` para HS*. Os bytes do token e a chave simplesmente não combinam.
Por que acontece
O Keyfunc retorna a chave errada
comumUma chave de outro ambiente, uma chave fixa no código copiada da documentação ou um Keyfunc que ignora o `kid` e sempre retorna a primeira chave de um conjunto de chaves, mesmo quando ele tem várias.
O emissor fez a rotação da chave de assinatura
comumServiços que carregam uma chave pública uma única vez na inicialização continuam verificando com a chave antiga depois de uma rotação. Todos os tokens novos falham, enquanto os tokens mais antigos em cache continuam passando, até que o serviço recarregue a chave.
O signatário produziu uma assinatura ECDSA em codificação DER
ocasionalO golang-jwt espera assinaturas ES256 como 64 bytes brutos (r‖s, RFC 7518). Um token assinado por um KMS ou por `ecdsa.SignASN1` sem conversão traz uma assinatura DER de ~71 bytes, cuja verificação falha mesmo com a chave certa.
O token foi alterado depois de assinado
ocasionalPayloads recodificados, mudanças de espaços em branco no segmento do cabeçalho ou do payload, ou um token montado com partes de dois tokens diferentes. A assinatura cobre o texto base64url exato.
Como corrigir
- 1.
Obter a chave do JWKS pelo kid
Deixe o Keyfunc escolher, no conjunto de chaves do emissor, a chave indicada no cabeçalho do token e atualizar esse conjunto na rotação (por exemplo, com `github.com/MicahParks/keyfunc`). Sempre fixe `WithValidMethods`.
go import ( "context" "github.com/MicahParks/keyfunc/v3" "github.com/golang-jwt/jwt/v5" ) // Resolve the verification key from the issuer's JWKS by the token's kid. k, err := keyfunc.NewDefaultCtx(ctx, []string{"https://issuer.example.com/.well-known/jwks.json"}) if err != nil { return err } token, err := jwt.Parse(raw, k.Keyfunc, jwt.WithValidMethods([]string{"ES256"}), jwt.WithIssuer("https://issuer.example.com/"), jwt.WithAudience("https://api.example.com")) - 2.
Comprovar que os dois lados usam a mesma chave
Calcule o hash da chave pública em codificação DER no verificador e o da metade pública da chave privada do emissor. Hashes diferentes encerram a investigação.
bash # Same key on both sides? Compare SPKI fingerprints. openssl pkey -pubin -in verifier-public.pem -outform DER | sha256sum openssl pkey -in issuer-private.pem -pubout -outform DER | sha256sum - 3.
Tratar a classe do erro, e não o texto
O golang-jwt v5 expõe erros sentinela. Mapeie uma falha de assinatura para 401 e não recorra a tentar outras chaves nem a pular a verificação.
go switch { case errors.Is(err, jwt.ErrTokenSignatureInvalid): return http.StatusUnauthorized // wrong key or tampered token: never retry with another key blindly case errors.Is(err, jwt.ErrTokenExpired): return http.StatusUnauthorized // client should refresh case errors.Is(err, jwt.ErrTokenMalformed): return http.StatusBadRequest }