本文へスキップ
ecdsa.com
golang-jwt/jwt (Go)Go

エラーメッセージ

token signature is invalid: crypto/ecdsa: verification error

別の表記

  • 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 は内側のテキストだけを返します(例:「crypto/ecdsa: verification error」)。文字列を照合するのではなく、`errors.Is(err, jwt.ErrTokenSignatureInvalid)`(v5)を使ってください。

golang-jwt/jwt (Go) — jwt.ErrTokenSignatureInvalid joined with the algorithm's verification error

このエラーの意味

golang-jwt はトークンを解析し、そのアルゴリズムを受け入れ、`Keyfunc` から鍵を取得しましたが、その鍵では署名を検証できませんでした。v5 のエラーは、`jwt.ErrTokenSignatureInvalid`(「token signature is invalid」)とアルゴリズム固有のエラーを結合したものです。ES* では `crypto/ecdsa: verification error`、RS*/PS* では `crypto/rsa: verification error`、HS* では `signature is invalid` になります。トークンのバイト列と鍵が、単に対応していないということです。

発生する理由

修正方法

  1. 1.

    kid で JWKS から鍵を選択する

    Keyfunc には、トークンのヘッダーで指定された鍵を発行者の鍵セットから選ばせ、鍵のローテーション時にはセットを更新させてください(たとえば `github.com/MicahParks/keyfunc` を使います)。`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. 2.

    両側が同じ鍵を使っていることを確かめる

    検証者側にある DER エンコードされた公開鍵と、発行者の秘密鍵に対応する公開鍵を、それぞれハッシュしてください。ハッシュ値が違えば、それ以上調べる必要はありません。

    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. 3.

    文字列ではなくエラーの種類で処理する

    golang-jwt v5 はセンチネルエラーを公開しています。署名の検証失敗は 401 にマッピングし、ほかの鍵を試したり検証を省略したりするフォールバックは行わないでください。

    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
    }

関連するエラー

署名エラーデータベースをすべて見る