如何获取 ADFS Public 密钥并验证 JWT 令牌上的签名?

How to obtain the ADFS Public Key and validate the signature on a JWT Token?

我需要验证在 ADFS 服务器上签名的 JWT 令牌的签名。我可以验证 self-signed JWT 令牌,但不能验证从 ADFS 收到的令牌。我应该如何获得 public 密钥?

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

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

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

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 class。)

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

参考this and this.

您指的按键:

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

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

您使用:

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

这有一个指向键的指针。