{"record":{"id":"b6d2cf8c04f5ae9a","repo":"hashicorp/nomad","slug":"invalid-signature-w","errorCode":null,"errorMessage":"invalid signature: %w","messagePattern":"invalid signature: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/encrypter.go","lineNumber":386,"sourceCode":"\t\treturn nil, err\n\t}\n\n\t// Find the key material\n\tpubKey, err := e.waitForPublicKey(keyID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttypedPubKey, err := pubKey.GetPublicKey()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tclaims := structs.IdentityClaims{}\n\n\t// Validate the claims.\n\tif err := token.Claims(typedPubKey, &claims); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid signature: %w\", err)\n\t}\n\n\t// COMPAT: Until we can guarantee there are no pre-1.7 JWTs in use, we can\n\t// only validate the signature and have no further expectations of the\n\t// claims.\n\tif err := claims.Validate(jwt.Expected{}); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid claims: %w\", err)\n\t}\n\n\treturn &claims, nil\n}\n\n// AddUnwrappedKey stores the key in the keystore and creates a new cipher for\n// it. This is called in the RPC handlers on the leader and from the legacy\n// KeyringReplicator.\nfunc (e *Encrypter) AddUnwrappedKey(rootKey *structs.UnwrappedRootKey, isUpgraded bool) (*structs.RootKey, error) {\n\n\t// note: we don't lock the keyring here but inside addCipher","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/encrypter.go#L368-L404","documentation":"The JWT parsed but its signature failed verification against the typed public key looked up by KeyID (token.Claims(typedPubKey, &claims)). The token was not signed by the keyring key corresponding to its kid header, or the key is no longer available.","triggerScenarios":"VerifyClaim resolves the kid to a public key in the keyring and calls token.Claims(pubKey, &claims); mismatched signing key, token signed by a removed/rotated key, or token from a different cluster produces this error.","commonSituations":"Tokens issued before a keyring rotation are presented after the old key was removed, multi-cluster setups where a token signed by cluster A's key is verified by cluster B, or the verifying server hasn't replicated the newer key yet.","solutions":["Issue a fresh token (re-run the workload or regenerate the claim) — the old key may have been rotated out","Ensure all servers share the same keystore/keys (keyring replication caught up)","Verify the token's kid matches a key that still exists: nomad keyring list","Check for cross-cluster token reuse and use the issuing cluster for verification"],"exampleFix":"// before: verifying a stale pre-rotation token\nclaims, err := encrypter.VerifyClaim(oldToken) // invalid signature\n// after: fetch a fresh signed claim for the allocation\ntoken, _, err := client.Allocations().GetAllocByID(allocID) // re-read identity token\nclaims, err := encrypter.VerifyClaim(token)","handlingStrategy":"try-catch","validationCode":"// check the kid exists in the current keyring before verification\nkeyring, _, err := client.Keyring().List(nil)\nif err != nil { return err }\nhaveKey := false\nfor _, k := range keyring {\n  if strings.HasPrefix(k.KeyID, sigAlgPrefixFromToken(token)) { haveKey = true }\n}\nif !haveKey { return fmt.Errorf(\"token signed with unknown/rotated-out key\") }","typeGuard":null,"tryCatchPattern":"claims, err := encrypter.VerifyClaim(token)\nif err != nil {\n  if strings.Contains(err.Error(), \"invalid signature\") {\n    // stale or foreign-cluster token: request a fresh token from the issuer\n    return fmt.Errorf(\"token not signed by a current keyring key — re-issue: %w\", err)\n  }\n  return err\n}","preventionTips":["Re-issue tokens after keyring rotations instead of caching them long-term","Never reuse workload identity tokens across clusters/regions","Keep keys in the keyring through at least the max token lifetime before removing them","Ensure all servers have replicated keystore data before retiring old keys"],"tags":["jwt","signature","jose","key-rotation","go"],"backgroundTag":"jwt-signature-verification-failed","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}