go-jose (Go)Go
错误信息
go-jose/go-jose: error in cryptographic primitive
也可能输出为
- square/go-jose: error in cryptographic primitive
go-jose v2(以 gopkg.in/square/go-jose.v2 导入)使用前缀“square/go-jose:”;v3 和 v4 使用“go-jose/go-jose:”。JWE 解密失败时返回的也是同一个错误。
go-jose (Go) — jose.ErrCryptoFailure from JSONWebSignature.Verify / jwt.JSONWebToken.Claims
含义
这是 go-jose 通用的签名(或解密)失败错误 `jose.ErrCryptoFailure`。JWS 已成功解析,算法也在允许范围内,但用你传入的密钥验证签名失败了。go-jose 有意不提供更多信息——在验证者看来,密钥错误和令牌被篡改是一样的——因此排查总是围绕两个问题:你传入的是哪个密钥?令牌有没有被改动?
原因
密钥不属于签名者
常见来自另一个环境或租户的密钥,或者颁发者在轮换时已经替换掉的公钥。
查找密钥集时没有使用 kid
常见取 `keys.Keys[0]` 而不是 `keys.Key(kid)` 的代码,会用密钥集中的第一个密钥验证。在颁发者发布第二个密钥之前,它都能正常工作。
头部或载荷被改动
偶见签名覆盖的是确切的 base64url 字节。重新序列化载荷,或者拼接两个令牌的各个部分,都会导致验证失败。
用错误的私钥解密
偶见对于 JWE 令牌,同一个错误表示内容加密密钥无法解包,或者认证标签验证失败——令牌是为另一个接收方密钥加密的。
修复方法
- 1.
按 kid 选择密钥,再校验声明
根据头部的 `kid` 从颁发者的 JWKS 中确定唯一一个密钥,验证签名,然后用 `Claims.Validate` 校验 `iss`、`aud` 和 `exp`——go-jose 不会自行检查声明。
go // keys: *jose.JSONWebKeySet fetched from the issuer's jwks_uri parsed, err := jwt.ParseSigned(raw, []jose.SignatureAlgorithm{jose.ES256}) if err != nil { return err } kid := parsed.Headers[0].KeyID matches := keys.Key(kid) // all keys with this kid if len(matches) != 1 { return fmt.Errorf("no unique key for kid %q", kid) } var claims jwt.Claims if err := parsed.Claims(matches[0].Key, &claims); err != nil { return err } return claims.Validate(jwt.Expected{Issuer: "https://issuer.example.com/", AnyAudience: jwt.Audience{"api"}, Time: time.Now()})