OpenNHP/opennhp · error

unexpected signing method

Error message

unexpected signing method: %v

What it means

During jwt.Parse, the keyfunc verifies the token's alg header is an ECDSA signing method; anything else (RS256, HS256, none) returns 'unexpected signing method: %v'. This prevents algorithm-confusion attacks where a token signed with a weaker or different algorithm is presented while the server holds an ECDSA key.

Solutions

  1. Fix the client to sign with ES256 (ECDSA P-256), matching the embedded P-256 JWK.
  2. Check the token's alg header offline to see what was actually used.
  3. Reject or route non-ECDSA tokens to the appropriate verification path instead of VerifyJWT.
  4. Ensure no client library fallback silently switches algorithms (explicitly set SigningMethodES256).
  5. If RS256 is expected from some clients, embed/use the matching RSA key rather than ECDSA in the keyfunc.

Example fix

// client side before: defaulting to another method
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// after
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
s, err := token.SignedString(ecdsaPrivateKey)
Defensive patterns

Strategy: validation

Validate before calling

func algIsECDSA(token string) bool {
	parts := strings.Split(token, ".")
	if len(parts) != 3 { return false }
	hdr, err := base64.RawURLEncoding.DecodeString(parts[0])
	if err != nil { return false }
	var h struct{ Alg string `json:"alg"` }
	if json.Unmarshal(hdr, &h) != nil { return false }
	return strings.HasPrefix(h.Alg, "ES")
}

Try / catch

token, err := VerifyJWT(rawToken)
if err != nil && strings.Contains(err.Error(), "unexpected signing method") {
	// wrong algorithm / possible alg-confusion: return 401, do not retry
}

Prevention

When it happens

Trigger: VerifyJWT receives a token whose header alg is not ES256-family ECDSA — e.g. RS256 tokens from an OIDC provider, HS256 tokens, or alg:none tokens — and jwt.Parse invokes the keyfunc.

Common situations: Client misconfigured to sign with RSA or HMAC instead of ES256; token issued by a generic auth service rather than the KBS attestation flow; crafted alg-confusion token from a probing client; library defaults changed between versions.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/3d27113884c65379. Report an issue: GitHub.

Appendix: source

Thrown at endpoints/server/kbs/resource/resource.go:254

	if err != nil {
		return nil, fmt.Errorf("invalid x coordinate: %w", err)
	}
	yBytes, err := base64.RawURLEncoding.DecodeString(yStr)
	if err != nil {
		return nil, fmt.Errorf("invalid y coordinate: %w", err)
	}

	publicKey := &ecdsa.PublicKey{
		Curve: elliptic.P256(),
		X:     new(big.Int).SetBytes(xBytes),
		Y:     new(big.Int).SetBytes(yBytes),
	}

	// Now verify the token with the extracted public key
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
		// Check signing method
		if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return publicKey, nil
	})

	if err != nil {
		return nil, err
	}

	return token, nil
}

View on GitHub (pinned to 6e04ca5ff0)