当前位置: 首页 > 知识库问答 >
问题:

如何验证此 ADFS 令牌?

寿高阳
2023-03-14

在我的MVC网站上,如果我检测到一个ADFS账户被使用,我会重定向到一个ADFS登录页面。在用户输入他们的ADFS凭据后,ADFS站点将< code>WsFederationMessage回发到我的站点。如何验证作为此< code>WsFederationMessage的一部分呈现给我的站点的ADFS令牌?

AuthenticationHandler 中间件类中,我有以下调用 ValidateToken 方法的相关代码:

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时,我遇到了这个错误:

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

异常详细信息:System.IdentityModel.SignatureVerificationFailedException: ID4037:无法从以下安全密钥标识符 'SecurityKeyIdentifier (
IsReadOnly = False, Count = 1, Clause[0] = X509RawDataKeyIdentifierClause(RawData = [由作者删除]中解析验证签名所需的密钥。确保使用所需的密钥填充安全令牌解决方案。

在搜索解决方案时,我找到了这篇文章,因此我使用该站点基于OpenSSL的解码器解码了上面代码中令牌字符串对象中的X509证书,因为它是中的PEM编码

将证书导入RP信任的签名选项卡

因此,我将签名证书添加到ADFS服务器上依赖方信任的签名选项卡中,在该选项卡中,我有一个机器标识符的信任规则。在这一切之后,它仍然不起作用。虽然有一点背景,但我的网站通过IIS在我的机器上本地运行,我已经更改了主机文件设置,使其指向<code>https://adfs-example.local/。我的ADFS服务器目前是VPN连接到我的站点的,所以我要说的是,ADFS服务器本身永远不会正确解析<code>的标识符https://adfs-example.local/如果它需要直接从这个URI请求一些东西,但是一旦它的浏览器重定向到我的站点的登录页面并提供了一个ADFS令牌,事情显然仍然有效。

把我被上帝抛弃的脑袋往墙上撞了一下,我试着添加我自己的< code > issuersigningkey resolver :

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进行签名。security token:“{ 0 }”。

说明:执行当前 Web 请求期间发生未处理的异常。请查看堆栈跟踪,了解有关错误及其在代码中的来源的详细信息。

例外详细信息:System.IdentityModel.Tokens。SecurityTokenValidationException:IDX10213:必须对SecurityTonks进行签名。SecurityToken:“{0}”。

源错误:

第 61 行:第 62 行:var validatedToken = (SecurityToken)null;Line 63: var principal = Options.SecurityTokenHandlers.ValidateToken(token, Options.TokenValidationParameters, out validatedToken);第 64 行:
var claimsIdentity = principal。标识为声明标识实体;第 65 行:
var ticket = new AuthenticationTicket(claimsIdentity, null);

该处理程序被调用两次。在第一次通话中,这似乎成功了。似乎第一个令牌已经签署。在第二次调用时,它失败了。第二个令牌似乎没有签名。为什么我的一些安全令牌没有签名?我如何进一步调试它?有人遇到过这种事情吗?

现在,我别无选择,只能检查源代码,所以我打开了AzureAD(也称为Wilson)的整个主干,我正在检查代码。它在SAML安全令牌处理程序的这一行失败:

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

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

编辑:再次检查ADFS服务器,我认为无论谁设置它都忘记了将私钥作为AD FS一部分的“令牌签名”和“令牌解密”证书的一部分 -

编辑:根据本文,这两个“令牌签名”和“令牌解密”证书应该是有效的,因为它们是自动生成的,只是它们的私钥存储在Active Directory中:

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

CN=ADFS,CN=微软,CN=程序数据,DC=域,DC=com

因此,要使 ADFS 安装将私钥安装到此位置,您必须是要安装 ADFS 的域管理员,或者具有为此容器分配的适当权限。

共有1个答案

叶晋
2023-03-14

最后,我放弃了AzureAD Nuget包,它无缘无故地引起了头痛。我采取了直接的方法。现在,只需要求我的 AD FS 服务器验证用户凭据即可。以下是代码(只需确保安装了Windows Identity Foundation SDK,并添加对Microsoft.IdentityModel,.dll,System.IdentityModel.dllSystem.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 = "username@domain.local"; // 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;
        }
    }
}

编辑:如果我想从AzureAD NuGet包中工作并继续重定向并使用他们的表单后请求解析器,我仍然可以使用上面的代码执行此操作。我仍然可以读取XAML令牌字符串并解析为有效的SecurityToken对象,如下所示:

var token = wsFederationMessage.GetToken();
var samlToken = handler.ReadToken(new XmlTextReader(new StringReader(token)));
ValidateSamlToken(samlToken);
 类似资料:
  • 我为OAuth2设置了ADFS3.0,我终于在我的客户端应用程序上得到了“访问令牌”。 像这样的事情: 令牌由标题部分、有效载荷和签名组成。 现在,我将请求与令牌一起发送到我的资源服务器。我想从我的资源服务器对ADFS(认证服务器和IDP)验证令牌。 这是我在adfs上的证书: 如何做到这一点? 更新:关于令牌的一些信息: 标题: 有效载荷: 签名: 计划授权授予流程(简短版本无授权授予代码详细信

  • 我可以通过HTTP-Redirect绑定登录和接收SAMLResponse,也可以使用privatekey解密和检索声明。 我的问题仍然是,我们需要验证saml响应(ADFS)吗?如果是如何做到这一点 我需要使用IP(身份提供商)公钥吗?它将在IP(元数据)中可用吗? 我在下面的请求参数中有SAML响应 SAMLACK=签名=hashvalue sigAlg=sha256 如何验证?

  • 我通过HTTP重定向从ADFS获得了SAML响应,并且在重定向时收到了以下请求参数

  • 通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者 可以全局设置开启令牌验证: <!--随机token令牌,使用UUID生成--> <dubbo:provider interface="com.foo.BarService" token="true" /> 或 <!--固定token令牌,

  • 文件上说https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html 必须验证访问令牌。如何验证令牌?