Mensaje de error
token signature is invalid: crypto/ecdsa: verification error
También se muestra 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
golang-jwt v4 solo devuelve el texto interno, por ejemplo «crypto/ecdsa: verification error». Usa `errors.Is(err, jwt.ErrTokenSignatureInvalid)` (v5) en lugar de comparar cadenas.
golang-jwt/jwt (Go) — jwt.ErrTokenSignatureInvalid joined with the algorithm's verification error
Qué significa
golang-jwt analiza el token, acepta su algoritmo y obtiene una clave de tu `Keyfunc`, pero la firma no se verifica con esa clave. En la v5, el error une `jwt.ErrTokenSignatureInvalid` («token signature is invalid») con el error propio del algoritmo: `crypto/ecdsa: verification error` para ES*, `crypto/rsa: verification error` para RS*/PS*, `signature is invalid` para HS*. Los bytes del token y la clave simplemente no corresponden entre sí.
Por qué ocurre
La Keyfunc devuelve la clave equivocada
frecuenteUna clave de otro entorno, una clave de la documentación fijada en el código o una Keyfunc que ignora `kid` y siempre devuelve la primera clave cuando el conjunto de claves contiene varias.
Rotación de la clave de firma del emisor
frecuenteLos servicios que cargan una clave pública una sola vez al arrancar siguen verificando con la clave antigua después de una rotación. Todos los tokens nuevos fallan, mientras que los tokens antiguos en caché siguen pasando, hasta que el servicio recarga la clave.
El firmante genera una firma ECDSA codificada en DER
ocasionalgolang-jwt espera las firmas ES256 como 64 bytes en bruto (r‖s, RFC 7518). Un token firmado por un KMS o con `ecdsa.SignASN1` sin conversión lleva una firma DER de unos 71 bytes, que no supera la verificación ni siquiera con la clave correcta.
Token modificado después de la firma
ocasionalCargas útiles recodificadas, cambios de espacios en blanco en el segmento del encabezado o de la carga útil, o un token montado con partes de dos tokens distintos. La firma cubre el texto base64url exacto.
Cómo solucionarlo
- 1.
Resolver la clave por kid desde el JWKS
Deja que la Keyfunc elija, del conjunto de claves del emisor, la clave que nombra el encabezado del token, y que actualice ese conjunto tras cada rotación (por ejemplo, con `github.com/MicahParks/keyfunc`). Fija siempre `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.
Demostrar que ambos lados usan la misma clave
Calcula el hash de la clave pública codificada en DER en el verificador y el de la mitad pública de la clave privada del emisor. Si los hashes difieren, la investigación termina ahí.
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.
Gestionar la clase de error, no la cadena
golang-jwt v5 expone errores centinela. Asigna a un fallo de firma una respuesta 401 y no recurras a probar otras claves ni a omitir la verificación.
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 }