如何validation此ADFS令牌?

在我的MVC站点上,如果我检测到正在使用ADFS帐户,我会重定向到ADFS登录页面。 用户输入其ADFS凭据后,ADFS站点WsFederationMessage回我的站点。 如何validation作为此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请求期间发生未处理的exception。 请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息。

exception详细信息:System.IdentityModel.SignatureVerificationFailedException:ID4037:无法从以下安全密钥标识符“SecurityKeyIdentifier”解析validation签名所需的密钥
IsReadOnly = False,Count = 1,Clause [0] = X509RawDataKeyIdentifierClause(RawData = [由作者删除]。确保使用所需的密钥填充SecurityTokenResolver。

搜索一个解决方案,我找到了这篇文章 ,所以我使用这个基于OpenSSL的解码器解码了上面代码中token字符串对象中的X509Certificate ,因为它是在 XAML标签中进行PEM编码的返回的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, "", true)[0]; store.Close(); var provider = (RSACryptoServiceProvider)cert.PublicKey.Key; return new RsaSecurityKey(provider); } }; 

现在我有一个错误的美丽,并不知道如何处理它:

IDX10213:必须签署SecurityTokens。 SecurityToken:'{0}’。

描述:执行当前Web请求期间发生未处理的exception。 请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息。

exception详细信息:System.IdentityModel.Tokens.SecurityTokenValidationException:IDX10213:SecurityTokens必须签名。 SecurityToken:'{0}’。

来源错误:

第61行:第62行:var validatedToken =(SecurityToken)null; 第63行:var principal = Options.SecurityTokenHandlers.ValidateToken(token,Options.TokenValidationParameters,out validatedToken); 第64行:
var claimsIdentity = principal.Identity as ClaimsIdentity; 第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 – >服务 – >证书选项卡的一部分ADFS管理单元。 但奇怪的是,通过与设置它的人交谈,显然它需要服务证书并吐出其他两个用于令牌签名和解密……但没有他们的私钥?

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

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

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

因此,对于将私钥安装到此位置的ADFS安装,您必须是域管理员才能安装ADFS或具有分配给此容器的相应权限。

最后,我删除了AzureAD Nuget软件包,它没有任何原因引起的头疼。 我采取直接的方法。 现在我只是简单地要求我的AD FS服务器validation用户凭据。 这是代码(只需确保安装了Windows Identity Foundation SDK ,并添加对Microsoft.IdentityModel.dllSystem.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);