📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 7.x-8.x

1. 错误异常的基本描述 #

Verify failed using X of Y provided JWKs. 表示 Elasticsearch 从全部 JWK 中筛出了一部分候选密钥,但逐个尝试后仍然没有一把能成功完成签名验证。

与"所有 JWK 都被过滤掉"不同,这里的筛选阶段已经通过,说明 kidusekty 等字段是匹配的,问题出在真正的密码学验签阶段。

常见现象 #

  • 日志中会出现类似 Verify failed using 0 of 3 provided JWKs. 的提示,其中第一个数字为实际验签成功的密钥数(通常为 0),第二个数字为参与验签的候选密钥总数。
  • Elasticsearch 安全日志中会伴随 security-tokenauthentication 相关警告,提示 JWT 令牌验证失败。
  • 使用 JWT 认证的客户端会收到 401 Unauthorized 响应,HTTP 返回头中常包含 WWW-Authenticate: Bearer realm="..." 信息。
  • Kibana 或自定义客户端在刷新 Token 后仍无法访问集群 API。

典型报错与异常栈 #

ElasticsearchSecurityException: Verify failed using 0 of 3 provided JWKs.
    at org.elasticsearch.xpack.security.authc.jwt.JwtRealm.verifySignature(JwtRealm.java:...)
    at org.elasticsearch.xpack.security.authc.jwt.JwtRealm.authenticate(JwtRealm.java:...)
Caused by: java.security.SignatureException: Signature length not correct
    at java.base/sun.security.rsa.RSASignature.engineVerify(...)

2. 为什么会发生这个错误 #

从异常语义看,筛选阶段已经通过,但真正调用验签逻辑时全部候选都失败了。这说明问题更可能出在签名与公钥本身不匹配,而不是过滤条件。

常见原因包括:

  • 公钥与私钥不匹配:JWK Set 中包含的公钥并非当前 Token 签发所用私钥对应的公钥,例如使用了过期的密钥对或错误的密钥文件。
  • 签名算法不一致:Token Header 中声明的 alg(如 RS256)与 JWK 中隐含的算法能力不符,或 Elasticsearch 配置中指定的 allowed_signature_algorithms 与实际情况冲突。
  • Token 内容被篡改:Token 的 Payload 或 Signature 部分在传输过程中被修改,导致任何合法公钥都无法通过验签。
  • JWK 格式不完整:JWK 缺少验签所需的关键字段(如 RSA 公钥缺少 ne),虽然能通过过滤阶段,但在实际验签时失败。
  • 密钥轮换不同步:身份提供商(IdP)已轮换签名密钥,但 Elasticsearch 侧配置的 JWK Set 尚未更新,导致新旧密钥不匹配。
  • JWK 来源混合错误:配置了多个 JWK 来源(如同时配置 pemjwkset_file),导致实际参与验签的密钥集合与预期不符。

3. 如何排查和解决这个异常 #

建议按"先解码 Token、再核对密钥、最后检查配置"的顺序处理:

  1. 解码 JWT Token:使用 jwt.io 或命令行工具解码 Token 的 Header 和 Payload,记录 algkidissaud 等关键字段。

    echo "eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyJ9..." | base64 -d | jq .
    
  2. 检查 JWK 候选集:确认 Elasticsearch 实际加载的 JWK Set 中是否包含与 Token kid 匹配且 alg 一致的密钥。

    curl -s -X GET "https://es-host:9200/_security/realm/<realm-name>/_jwks" | jq .
    
  3. 比对公钥与私钥:使用 OpenSSL 提取 JWK 中的公钥信息,与签发侧的私钥进行配对验证。

    # 将 JWK 中的 n 和 e 转换为 PEM 格式公钥,与签发侧证书比对
    openssl rsa -pubin -text -noout < public_key.pem
    
  4. 验证 Token 完整性:确认 Token 在传输过程中未被网关、代理或日志脱敏组件改写(注意检查是否有 = 填充被截断的情况)。

  5. 查看完整安全日志:在 Elasticsearch 中搜索相关时间点的安全审计日志,确认是否有更多上下文信息。

    grep -i "jwt\|jwk\|verify" /var/log/elasticsearch/security-audit.log
    

排查时需要注意的问题 #

  • 不要只看 X of Y 中的数字,需要结合具体 JWK 的 kidalg 逐一核对,确认是哪一把密钥实际参与了验签。
  • 如果使用了 JWK 远程获取(jwkset_path),需要确认网络可达性以及返回内容是否是最新版本,而非缓存的旧数据。
  • 注意 Elasticsearch 的 JWT Realm 配置中 allowed_issuerallowed_audiences 是否与 Token 中的声明一致,虽然这不直接导致验签失败,但会掩盖真正的问题。

4. 如何解决这个错误 #

常用修复思路 #

  • 更新 JWK Set:确保 jwkset_filejwkset_path 指向的 JWK 包含与当前签发私钥对应的公钥,必要时重新从 IdP 下载最新的 JWK Set。
  • 修正签名算法配置:在 JWT Realm 配置中明确指定 allowed_signature_algorithms,避免签发侧与校验侧算法不一致。
    xpack.security.authc.realms.jwt.realm_name:
      order: 1
      allowed_signature_algorithms: [RS256, RS384, RS512]
      jwkset_file:
        path: "/path/to/jwks.json"
    
  • 保护 Token 传输链路:在网关或代理中配置不修改请求头,避免对 Authorization 头进行 base64 解码重组或截断处理。
  • 启用 JWK 缓存刷新:如果 JWK 会定期轮换,配置合理的缓存刷新策略,避免本地缓存中的 JWK 过期后仍被使用。

后续注意事项与推荐建议 #

  • 建立 JWK 轮换的监控机制,在 IdP 轮换密钥后及时收到通知并更新 Elasticsearch 配置。
  • 为 JWT 认证配置多个 JWK 来源或使用 jwkset_path 指向 IdP 的动态端点,减少手动更新带来的滞后。
  • 在测试环境中使用已知的 JWK 对 Token 签发和验证做端到端验证,确保整个链路在密钥轮换前后均可正常工作。

借助 INFINI 产品提升排障效率 #

  • INFINI Console 适合查看集群安全配置、认证日志和请求画像,帮助快速判断 JWT 验证失败的影响范围。
  • INFINI Gateway 适合部署在 Elasticsearch 前面做请求观测和流量治理,可以在 Token 到达 Elasticsearch 之前捕获并解码 JWT,辅助定位验签失败的根本原因。

5. 小结 #

Verify failed using X of Y provided JWKs. 说明 Elasticsearch 确实尝试过用候选密钥验签,但都失败了。与"全部被过滤"相比,它更接近公钥不匹配、签名算法不一致或 Token 被改动的问题。排查时应优先核对 JWK 内容与 Token 的 kid/alg 是否完全匹配,并确认密钥对的配对关系正确无误。

相关错误 #

附:日志上下文 #

throw new ElasticsearchSecurityException(
    "Verify failed using {} of {} provided JWKs.",
    jwksStrength.size(),
    providedJwks.size()
);