首页 > 解决方案 > 如何获取 ADFS 公钥并验证 JWT 令牌上的签名?

问题描述

我需要验证在 ADFS 服务器上签名的 JWT 令牌的签名。我可以验证自签名 JWT 令牌,但不能验证从 ADFS 收到的令牌。我应该如何获取公钥?

为了从 ADFS 服务器获取公钥,我让我的同事从 ADFS 服务器导出证书。在 ADFS 控制台上,他查看了“服务”>“编辑联合服务属性”、“常规”选项卡,在那里他找到了三个整体。它们位于“令牌签名”、“令牌解密”和“服务通信”标题下。对于每个人,他都查看了证书,然后导出了 DER 证书(没有私钥)。然后将它们转换为 PEM(使用 openssl x509 -inform der -in cert.cer -out cert.pem),但这些密钥都不允许我的代码验证嗅探到的 JWT 令牌。

我的 Java 代码采用 JWT 令牌和公钥,并验证令牌是否使用公钥签名。如果我使用自签名密钥对和自生成 JWT 令牌,则代码似乎可以工作,并报告签名正常。当令牌从来自 ADFS 的消息中的 HTTP 标头复制时,相同的代码会报告签名无效。

这是我用来加载 PEM 公钥的代码:

FileInputStream fis = new FileInputStream(publicKeyFile)
Reader keyReader = new InputStreamReader(publicKeyStream);
PemReader pemReader = new PemReader(keyReader);
PEMParser pemParser = new PEMParser(pemReader);
Object pemObject = pemParser.readObject();
if (pemObject instanceof X509CertificateHolder) {
  X509CertificateHolder x509CertificateHolder = (X509CertificateHolder) pemObject;
  X509Certificate x509Certificate = new JcaX509CertificateConverter()
    .setProvider("BC").getCertificate(x509CertificateHolder);
  return x509Certificate.getPublicKey();
}

这是我用来验证签名的代码:

String signatureAlgorithm = publicKey.getAlgorithm();
Signature signatureInstance = Signature.getInstance(signatureAlgorithm);
signatureInstance.initVerify(publicKey);
byte[] messageBytes = Base64.encodeBase64(message.getBytes(UTF_8));
signatureInstance.update(messageBytes);
byte[] receivedSignature = Base64.decodeBase64URLSafe(signature);
return signatureInstance.verify(receivedSignature);

(为简洁起见,我删除了上面的异常处理和资源关闭。我使用的是 tomcat 的 Base64 类。)

代码运行没有错误,但表明签名无效。在我看来,以下任何一项都可能是错误的:

标签: jwtadfssignaturejava.security

解决方案


参考这个这个

您引用的密钥:

  • “令牌签名” - 用于从 CP 或 RP 信任派生的 SAML 令牌,例如通过 SAML 或 WS-Fed 进行的联合
  • “令牌解密” - 同样
  • “服务通信” - 用于服务器 SSL 通信

对于 JWT,我假设您使用的是 OpenId Connect?

你用:

https://[您的 ADFS 主机名]/adfs/.well-known/openid-configuration

这有一个指向键的指针。


推荐阅读