在我的 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 或拥有
分配给该容器的适当权限。