如何验证此 ADFS 令牌?

2024-01-18

在我的 MVC 站点上,如果检测到正在使用 ADFS 帐户,我会重定向到 ADFS 登录页面。用户输入 ADFS 凭据后,ADFS 站点会发布WsFederationMessage返回我的网站。如何验证在此过程中向我的网站提供的 ADFS 令牌WsFederationMessage?

里面一个AuthenticationHandler中间件类,我有以下相关代码,它调用ValidateToken method:

IFormCollection form = await Request.ReadFormAsync();

WsFederationMessage wsFederationMessage = new WsFederationMessage(form);

if (!wsFederationMessage.IsSignInMessage)
{
    Request.Body.Seek(0, SeekOrigin.Begin);
    return null;
}

var token = wsFederationMessage.GetToken();

if (wsFederationMessage.Wresult != null && Options.SecurityTokenHandlers.CanReadToken(token))
{
    SecurityToken validatedToken;
    ClaimsPrincipal principal = Options.SecurityTokenHandlers.ValidateToken(token, Options.TokenValidationParameters, out validatedToken);
    ...
}

当我尝试致电时出现此错误ValidateToken:

描述:执行期间发生未处理的异常。 当前的网络请求。请查看堆栈跟踪以了解更多信息 有关错误及其在代码中的来源的信息。

异常详细信息: System.IdentityModel.SignatureVerificationFailedException:ID4037: 验证签名所需的密钥无法从 以下安全密钥标识符“SecurityKeyIdentifier”(
IsReadOnly = False,计数 = 1,子句[0] = X509RawDataKeyIdentifierClause(RawData = [已被作者删除]。确保 SecurityTokenResolver 已填充所需的密钥。

寻找解决方案,我发现本文 http://social.technet.microsoft.com/wiki/contents/articles/1420.ad-fs-2-0-id4037-the-key-needed-to-verify-the-signature-could-not-be-resolved-from-the-following-security-key-identifier.aspx,所以我解码了X509Certificate内提出的token我上面的代码中的字符串对象使用本网站基于 OpenSSL 的解码器 https://www.sslshopper.com/certificate-decoder.html,因为它是 PEM 编码的<X509Certificate></X509Certificate>返回的XAML标签token细绳。事实上,这就是决议文章所说的签名证书。因此,我进入我的 ADFS 服务器,将签名证书导出为公共证书并将其安装在我的网站上Trusted Root Certificate Authorities。该链接还提到我必须:

将证书导入到 RP Trust 的“签名”选项卡

因此,我将签名证书添加到 ADFS 服务器上依赖方信任的“签名”选项卡中,在该服务器上我有一个针对计算机标识符的信任规则。做了这一切之后,仍然没有成功。不过,有一点背景知识,我的网站通过 IIS 在我的计算机上本地运行,并且我更改了主机文件设置以使其指向https://adfs-example.local/。我的 ADFS 服务器目前通过 VPN 连接到我的站点,所以我的意思是 ADFS 服务器本身永远无法正确解析https://adfs-example.local/如果它需要直接从此 URI 请求某些内容,但一旦浏览器重定向到我网站的登录页面并提供 ADFS 令牌,事情显然仍然有效。

将我的上帝遗弃的头再撞到墙上,我尝试添加我自己的头IssuerSigningKeyResolver:

TokenValidationParameters = new TokenValidationParameters
{
    IssuerSigningKeyResolver = (token, securityToken, keyIdentifier, validationParameters) =>
    {
        var store = new X509Store(StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadOnly);
        var cert = store.Certificates.Find(X509FindType.FindByThumbprint, "<My Certificate's Thumbprint>", true)[0];
        store.Close();
        var provider = (RSACryptoServiceProvider)cert.PublicKey.Key;
        return new RsaSecurityKey(provider);
    }
};

现在我遇到了一个美丽的错误,但不知道如何处理它:

IDX10213:SecurityTokens 必须经过签名。安全令牌:“{0}”。

描述:执行期间发生未处理的异常。 当前的网络请求。请查看堆栈跟踪以了解更多信息 有关错误及其在代码中的来源的信息。

异常详细信息: System.IdentityModel.Tokens.SecurityTokenValidationException: IDX10213:SecurityTokens 必须经过签名。安全令牌:“{0}”。

来源错误:

第 61 行:第 62 行:var validatedToken = (安全令牌)空;第 63 行:var 主体 = Options.SecurityTokenHandlers.ValidateToken(令牌, Options.TokenValidationParameters, out validatedToken);第 64 行:
var ClaimsIdentity = 主体.Identity as ClaimsIdentity;第 65 行:
var Ticket = new AuthenticationTicket(claimsIdentity, null);

处理程序被调用两次。在第一次通话中,这似乎成功了。看来第一个令牌已签名。第二次调用时,失败。看来第二个令牌没有签名。为什么我的一些安全令牌没有签名?我怎样才能进一步调试这个?有人必须处理过这样的事情吗?

现在我别无选择,只能检查来源,所以我拉了整个主干AzureAD https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet(也称为 Wilson),我正在查看代码。它在 SAML 安全令牌处理程序的这一行失败:

if (samlToken.Assertion.SigningToken == null && validationParameters.RequireSignedTokens)
{
    throw new SecurityTokenValidationException(ErrorMessages.IDX10213);
}

我不明白。这意味着签名令牌为空。为什么签名令牌为空?

Edit:再次检查 ADFS 服务器,我认为设置它的人忘记将私钥包含在“令牌签名”和“令牌解密”证书中,这些证书是 AD FS -> 服务 -> 证书选项卡的一部分ADFS 管理单元。但奇怪的是,通过与设置它的人交谈,显然它需要服务证书并吐出另外两个用于令牌签名和解密......但是没有他们的私钥?

Edit: 根据本文 http://blogs.technet.com/b/askpfeplat/archive/2015/01/26/adfs-deep-dive-certificate-planning.aspx,这两个“令牌签名”和“令牌解密”证书应该是有效的,因为它们是自动生成的,只是它们的私钥存储在 Active Directory 中:

当您使用自签名证书进行令牌签名时 解密时,私钥存储在 Active Directory 中 以下容器:

CN=ADFS、CN=Microsoft、CN=程序数据、DC=域、DC=com

因此,对于 ADFS 安装,需要安装私钥 进入此位置,您必须是域管理员才能安装 ADFS 或拥有 分配给该容器的适当权限。


最后,我放弃了 AzureAD Nuget 包,它只会无缘无故地让人头疼。我采取了直接的方法。现在,我只需要求我的 AD FS 服务器验证用户凭据。这是代码(只要确保有Windows 身份基础 SDK http://www.microsoft.com/en-ca/download/details.aspx?id=4451安装,并添加引用Microsoft.IdentityModel.dll, System.IdentityModel.dll, and System.ServiceModel.dll):

using System;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Xml;
using Microsoft.IdentityModel.Protocols.WSTrust;
using Microsoft.IdentityModel.Protocols.WSTrust.Bindings;

namespace ADFSFederationToken
{
    class Program
    {

        static string _relyingPartyIdentifier = "https://yourapplication.local/"; // Must be whatever you specified on your AD FS server as the relying party address.
        static string _adfsServerAddress = "https://adfs.example.local/"; // Your ADFS server's address.
        static string _username = "[email protected] /cdn-cgi/l/email-protection"; // A username to your ADFS server.
        static string _password = "password"; // A password to your ADFS server.
        static string _signingCertificateThumbprint = "1337..."; // Put the public ADFS Token Signing Certificate's thumbprint here and be sure to add it to your application's trusted certificates in the Certificates snap-in of MMC.
        static string _signingCertificateCommonName = "ADFS Signing - adfs.example.local"; // Put the common name of the ADFS Token Signing Certificate here.

        static void Main(string[] args)
        {
            Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory factory = null;
            try
            {
                _relyingPartyIdentifier = _relyingPartyIdentifier.EndsWith("/") ? _relyingPartyIdentifier : _relyingPartyIdentifier + "/";
                _adfsServerAddress = _adfsServerAddress.EndsWith("/") ? _adfsServerAddress : _adfsServerAddress + "/";
                factory = new Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), new EndpointAddress(_adfsServerAddress + "adfs/services/trust/13/usernamemixed"));
                factory.TrustVersion = TrustVersion.WSTrust13;
                factory.Credentials.UserName.UserName = _username;
                factory.Credentials.UserName.Password = _password;
                var rst = new Microsoft.IdentityModel.Protocols.WSTrust.RequestSecurityToken
                {
                    RequestType = WSTrust13Constants.RequestTypes.Issue,
                    AppliesTo = new EndpointAddress(_relyingPartyIdentifier),
                    KeyType = WSTrust13Constants.KeyTypes.Bearer
                };
                var channel = factory.CreateChannel();
                var genericToken = channel.Issue(rst) as GenericXmlSecurityToken;
                var handler = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
                var tokenString = genericToken.TokenXml.OuterXml;
                var samlToken = handler.ReadToken(new XmlTextReader(new StringReader(tokenString)));
                ValidateSamlToken(samlToken);
            }
            finally
            {
                if (factory != null)
                {
                    try
                    {
                        factory.Close();
                    }
                    catch (CommunicationObjectFaultedException)
                    {
                        factory.Abort();
                    }
                }
            }
        }

        public static ClaimsIdentity ValidateSamlToken(SecurityToken securityToken)
        {
            var configuration = new SecurityTokenHandlerConfiguration();
            configuration.AudienceRestriction.AudienceMode = AudienceUriMode.Always;
            configuration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(_relyingPartyIdentifier));
            configuration.CertificateValidationMode = X509CertificateValidationMode.ChainTrust;
            configuration.RevocationMode = X509RevocationMode.Online;
            configuration.CertificateValidator = X509CertificateValidator.ChainTrust;
            var registry = new ConfigurationBasedIssuerNameRegistry();
            registry.AddTrustedIssuer(_signingCertificateThumbprint, _signingCertificateCommonName);
            configuration.IssuerNameRegistry = registry;
            var handler = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection(configuration);
            var identity = handler.ValidateToken(securityToken).First();
            return identity;
        }
    }
}

Edit:如果我想使用 AzureAD NuGet 包并继续重定向并使用其表单发布请求解析器,我仍然可以使用上面的代码来完成此操作。我仍然可以读取 XAML 令牌字符串并解析为有效的SecurityToken像这样的对象:

var token = wsFederationMessage.GetToken();
var samlToken = handler.ReadToken(new XmlTextReader(new StringReader(token)));
ValidateSamlToken(samlToken);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何验证此 ADFS 令牌? 的相关文章

随机推荐

  • 加密 SQL 数据库中的密码列

    我的数据库名称密码中有一列 我只想在发布到数据库之前对密码进行哈希或加密 我的 php 提交文件中有这样的代码
  • 在 Python 中处理字符串中的转义序列

    有时 当我从文件或用户获取输入时 我会得到一个带有转义序列的字符串 我想处理转义序列与 Python 处理字符串文字中的转义序列的方式相同 https docs python org 3 reference lexical analysis
  • 在 jQuery 中创建新的(永久)CSS 样式

    我想创建一种新样式 而不仅仅是更改元素的样式属性 下面是一些示例代码来演示该问题 Create 1st element var element1 div div text element1 addClass blue body append
  • ASP.NET c# 修复函数库内自定义ConfirmMessagebox 的 OK 事件

    我们创建了一个从 C 通过 javascript 动态显示模态弹出消息的函数 它工作正常 但我们想添加一个参数 以便我们可以传递一个函数委托 或事件处理程序 如果用户按下 确定 按钮 该函数将被调用 有什么建议么 Postdata 我们不需
  • 函数调用之外的星号

    我正在尝试 python 我有一个关于星号的问题 我知道它们用于函数调用中的参数 但我见过在函数卡之外使用它们的代码片段 例如 在 5 个等级的元组中 将它们解压到变量中 例如 first middle last grades 每当我尝试在
  • Chart.js stepSize 不适用于 min

    StepSize 不考虑 min 包括小提琴 https jsfiddle net 4p93aew7 10 https jsfiddle net 4p93aew7 10 var options type line data labels R
  • 在 CSS 中为 div 使用多个 ID

    我的网站上有 3 个 DIV 元素和这个 CSS box left box middle box right a text decoration none color 000000 它似乎只致力于 box right虽然元素 有任何想法吗
  • 将动态内容放入 mail() $message 中

    我只是想整理一封简单的 HTML 电子邮件 以确认我的数据库的订单 我的 message 看起来有点像 message etc etc 我想要在 message HTML 中执行的操作是调用我的数据库 该数据库将返回类似以下内容的行 ema
  • 使用 Facelets 标记文件时出现 FileNotFoundException

    我正在尝试运行 Core JavaServer Faces 书中的示例 ch05 http horstmann com corejsf http horstmann com corejsf 登录后我收到以下消息 sections plane
  • 实现 std::basic_streambuf 子类来操作输入

    我有一个std basic streambuf导致所有输出都以大写形式写入的子类 如下所示 class upper streambuf public std streambuf public upper streambuf std stre
  • 如何从相机拍摄的照片制作缩略图?

    在我的应用程序中 我使用外部相机应用程序来制作照片并将其保存到 SD 卡 我需要这张照片的方形缩略图 我已经设法将相机发送回的缩略图裁剪成正方形 但由于我将照片保存到磁盘 因此意图是空的 而我的旧方法使图片成为正方形Bitmap cropp
  • Spring单例bean的这种设计是线程安全的吗?

    考虑以下 Spring 服务类 spring定义的作用域是Singleton 在下面的类中自动连接为字段的两个服务 bean 具有相似的结构 它们也由以下字段之一组成 春豆本身 无状态类 不可变类 等等 该模式在应用程序设计中总体采用 Se
  • AjaxControlToolkit.CommonToolkitScripts 错误

    我在我的 ASP NET 应用程序中得到以下代码
  • 回复 Gmail 线程会向我自己发送电子邮件

    文档 https developers google com apps script reference gmail gmail message replybody options https developers google com a
  • 当顶点属性数组零被禁用时,为什么 OpenGL 绘制会失败?

    我在让我的顶点着色器在 ATI 驱动程序上的 OpenGL 3 3 核心下运行时遇到了极大的困难 version 150 uniform mat4 graph matrix view matrix proj matrix uniform b
  • MySQL JOIN 与多个表和 SUMS

    我正在尝试创建一个查询 该查询将从我正在创建的计费系统的四个表中获取信息 我有以下表格 表发票 InvoiceID PK ClientID Date Status 桌面客户端 ClientID PK ClientName 表发票项目 Ite
  • 能否对 IO 操作进行排序,同时将逻辑保持在纯函数中?

    我有以下代码 它从分页 API 端点获取两页数据 我想修改query函数不断获取页面 直到找不到更多数据 因此替换take 2在下面的代码中查看 API 响应 我的问题是是否可以在不改变的情况下实现这一目标query函数到IO功能 如果是这
  • 将音频与视频结合起来 - Android

    我正在开发一个应用程序来捕获视频和下载音频 我可以单独保存这些文件 但找不到将这些文件组合起来制作新的音频视频的方法 视频文件为 mp4 格式 音频为 mp3 格式 它们的长度完全相同 有什么办法可以合并这些文件吗 我已经尝试过 但找不到合
  • 无法在 jframe 内的 jscrollpane 中添加图像

    我正在尝试使用 jscroll 窗格将图像添加到我的 jframe 中 我尝试了一些方法 但图像仅出现在背景中 像这样的 下面的图像编辑器 这是我的代码 private void initComponents jScrollPane1 ne
  • 如何验证此 ADFS 令牌?

    在我的 MVC 站点上 如果检测到正在使用 ADFS 帐户 我会重定向到 ADFS 登录页面 用户输入 ADFS 凭据后 ADFS 站点会发布WsFederationMessage返回我的网站 如何验证在此过程中向我的网站提供的 ADFS