Ale*_*dru 6 c# asp.net asp.net-mvc ssl adfs
在我的MVC站点上,如果我检测到正在使用ADFS帐户,我会重定向到ADFS登录页面.用户输入其ADFS凭据后,ADFS站点将WsFederationMessage回发到我的站点.如何验证作为其中一部分呈现给我的站点的ADFS令牌WsFederationMessage?
在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);
...
}
Run Code Online (Sandbox Code Playgroud)
我试图打电话时收到此错误ValidateToken:
描述:执行当前Web请求期间发生未处理的异常.请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息.
异常详细信息:System.IdentityModel.SignatureVerificationFailedException:ID4037:无法从以下安全密钥标识符'SecurityKeyIdentifier解析验证签名所需的密钥(
IsReadOnly = False,Count = 1,Clause [0] = X509RawDataKeyIdentifierClause(RawData = [已删除]作者].确保使用所需的密钥填充SecurityTokenResolver.
搜索一个分辨率,我找到了这篇文章,所以我使用这个网站的基于OpenSSL的解码器解码了上面代码中X509Certificate的token字符串对象中的呈现,因为它是在返回字符串的XAML标记内进行PEM编码.事实上,正如决议文章所述,这是签署证书.所以我继续使用我的ADFS服务器,将签名证书导出为公共证书并将其安装在我的网站上.该链接还提到我必须:<X509Certificate></X509Certificate>tokenTrusted 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);
}
};
Run Code Online (Sandbox Code Playgroud)
现在我有一个错误的美丽,并不知道如何处理它:
IDX10213:必须签署SecurityTokens.SecurityToken:'{0}'.
描述:执行当前Web请求期间发生未处理的异常.请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息.
异常详细信息: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);
}
Run Code Online (Sandbox Code Playgroud)
我不明白.这意味着签名令牌为空.为什么签名令牌为空?
编辑:再次检查ADFS服务器,我想无论谁设置它都忘记包含私钥作为"令牌签名"和"令牌解密"证书的一部分,这些证书是AD FS - >服务 - >证书选项卡的一部分ADFS管理单元.但奇怪的是,通过与设置它的人交谈,显然它需要服务证书并吐出其他两个用于令牌签名和解密......但没有他们的私钥?
编辑:根据这篇文章,这两个"令牌签名"和"令牌解密"证书应该是有效的,因为它们是自动生成的,只是他们的私钥存储在Active Directory中:
使用自签名证书进行令牌签名和解密时,私钥存储在以下容器的Active Directory中:
CN = ADFS,CN = Microsoft,CN =程序数据,DC =域,DC = com
因此,要将ADFS安装到此位置安装私钥,您必须是域管理员才能安装ADFS或将相应的权限分配给此容器.
最后,我放弃了AzureAD Nuget软件包,它没有引起任何原因,但没有任何理由.我采取直接的方法.现在我只是简单地要求我的AD FS服务器验证用户凭据.下面是代码(只是一定要拥有的Windows标识基础SDK安装,并添加引用Microsoft.IdentityModel.dll,System.IdentityModel.dll和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 = "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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:如果我想从AzureAD NuGet包工作并继续重定向并使用他们的表单发布请求解析器,我仍然可以使用上面的代码.我仍然可以读取XAML标记字符串并解析为有效SecurityToken对象,如下所示:
var token = wsFederationMessage.GetToken();
var samlToken = handler.ReadToken(new XmlTextReader(new StringReader(token)));
ValidateSamlToken(samlToken);
Run Code Online (Sandbox Code Playgroud)