如何在ASP.NET Core Web API中验证ADFS 3.0的SAML令牌
ASP.NET Core 6验证ADFS 3.0颁发的SAML令牌问题
问题背景
从Windows Server 2012R2上的ADFS 3.0实例获取SAML 2.0令牌,通过请求头发送到ASP.NET Core 6 Web API进行验证时遇到签名验证失败问题。现有.NET Framework 4.5 API可正常验证该令牌,但相关依赖无法直接迁移到.NET Core。
令牌获取代码(.NET Framework)
WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory(new WindowsWSTrustBinding(SecurityMode.TransportWithMessageCredential), new EndpointAddress(new Uri(url))); trustChannelFactory.TrustVersion = TrustVersion.WSTrust13; trustChannelFactory.Credentials.Windows.ClientCredential.Domain = domain; trustChannelFactory.Credentials.Windows.ClientCredential.UserName = username; trustChannelFactory.Credentials.Windows.ClientCredential.Password = password; RequestSecurityToken rst = new RequestSecurityToken(WSTrust13Constants.RequestTypes.Issue, WSTrust13Constants.KeyTypes.Bearer); rst.AppliesTo = new EndpointAddress(relyingParting); rst.TokenType = Microsoft.IdentityModel.Tokens.SecurityTokenTypes.Saml2TokenProfile11; WSTrustChannel channel = (WSTrustChannel)trustChannelFactory.CreateChannel(); GenericXmlSecurityToken token = channel.Issue(rst) as GenericXmlSecurityToken; tokenString = token.TokenXml.OuterXml; return tokenString;
令牌格式示例
<Assertion ID="_1d020948-dd8c-4349-9b4e-94832d2381b3" IssueInstant="2024-04-29T19:18:26.948Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"> <Issuer>http://my-adfs-instance.net/adfs/services/trust</Issuer> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /> <ds:Reference URI="#_1d020948-dd8c-4349-9b4e-94832d2381b3"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /> <ds:DigestValue>NoClueIfSensitive</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue>Redacted</ds:SignatureValue> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <ds:X509Data> <ds:X509Certificate>MII...</ds:X509Certificate> </ds:X509Data> </KeyInfo> </ds:Signature> <Subject> <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <SubjectConfirmationData NotOnOrAfter="2024-04-29T19:23:26.948Z" /> </SubjectConfirmation> </Subject> <Conditions NotBefore="2024-04-29T19:18:26.653Z" NotOnOrAfter="2024-04-29T20:18:26.653Z"> <AudienceRestriction> <Audience>https://my-app.net</Audience> </AudienceRestriction> </Conditions> <AttributeStatement> ... </AttributeStatement> <AuthnStatement AuthnInstant="2024-04-29T19:18:26.590Z"> ... </AuthnStatement> </Assertion>
已尝试的验证方法及异常
方法1:直接使用Saml2SecurityTokenHandler.ValidateToken
var headers = Request.Headers; var token = headers["token"]; var tokenHandler = new Saml2SecurityTokenHandler(); var tokenValidationParameters = new TokenValidationParameters() { ValidIssuer = _config.GetSection("TokenValidation")["ValidIssuer"], ValidAudience = _config.GetSection("TokenValidation")["ValidAudience"] }; tokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken validatedToken);
IDX10500: Signature validation failed. No security keys were provided to validate the signature
方法2:添加IssuerSigningKey(ADFS签名证书)
var tokenHandler = new Saml2SecurityTokenHandler(); var issuerSigningCert = new X509Certificate2("C:\\signingCert.cer"); var securityKey = new X509SecurityKey(issuerSigningCert); var tokenValidationParameters = new TokenValidationParameters() { ValidIssuer = _config.GetSection("TokenValidation")["ValidIssuer"], ValidAudience = _config.GetSection("TokenValidation")["ValidAudience"], IssuerSigningKey = securityKey }; tokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken validatedToken);
IDX10514: Signature validation failed
方法3:尝试SymmetricSecurityKey
var issuerSigningCert = new X509Certificate2("C:\\signingCert.cer"); var securityKey = new X509SecurityKey(issuerSigningCert); var tokenValidationParameters = new TokenValidationParameters() { ValidIssuer = _config.GetSection("TokenValidation")["ValidIssuer"], ValidAudience = _config.GetSection("TokenValidation")["ValidAudience"], IssuerSigningKey = new SymmetricSecurityKey(issuerSigningCert.GetPublicKey()) };
IDX10512: Signature validation failed. Token does not have KeyInfo.
核心问题(开启ShowPII后发现)
IDX30207: SignatureMethod is not supported: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'. CryptoProviderFactory: 'Microsoft.IdentityModel.Tokens.CryptoProviderFactory'.
解决方案建议
方案1:配置CryptoProviderFactory支持RSA-SHA256算法
默认的CryptoProviderFactory未包含对http://www.w3.org/2001/04/xmldsig-more#rsa-sha256的支持,需手动添加或启用:
// 全局启用ShowPII(仅调试用,生产环境禁用) IdentityModelEventSource.ShowPII = true; // 配置CryptoProviderFactory var cryptoProviderFactory = new CryptoProviderFactory(); // 添加对RSA-SHA256签名算法的支持 cryptoProviderFactory.SupportedAlgorithms.Add( "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", typeof(RsaSecurityKey) ); // 加载ADFS签名证书(公钥证书即可) var issuerSigningCert = new X509Certificate2("path/to/adfs-signing.cer"); var tokenHandler = new Saml2SecurityTokenHandler(); // 绑定自定义CryptoProviderFactory到handler tokenHandler.CryptoProviderFactory = cryptoProviderFactory; var tokenValidationParameters = new TokenValidationParameters { ValidIssuer = "http://my-adfs-instance.net/adfs/services/trust", ValidAudience = "https://my-app.net", // 使用IssuerSigningKeys列表而非单个Key,便于handler匹配 IssuerSigningKeys = new List<SecurityKey> { new X509SecurityKey(issuerSigningCert) }, ValidateIssuerSigningKey = true, ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true }; try { var claimsPrincipal = tokenHandler.ValidateToken(tokenString, tokenValidationParameters, out var validatedToken); // 验证成功,处理ClaimsPrincipal } catch (Exception ex) { // 处理验证异常 }
方案2:使用第三方SAML库(ITfoxtec.Identity.Saml2)
该库专门针对SAML 2.0协议,对旧版本ADFS兼容性更好:
- 安装NuGet包:
Install-Package ITfoxtec.Identity.Saml2.AspNetCore
- 在Program.cs中配置SAML服务:
builder.Services.AddSaml2(options => { // 添加ADFS签名证书 options.SignatureValidationCertificates.Add(new X509Certificate2("path/to/adfs-signing.cer")); // 设置当前API的Issuer(与令牌中的Audience一致) options.Issuer = "https://my-app.net"; // 添加允许的Issuer(ADFS实例地址) options.AllowedIssuers.Add("http://my-adfs-instance.net/adfs/services/trust"); });
- 在控制器中验证令牌:
[HttpPost] public IActionResult ValidateSamlToken([FromHeader] string token) { var saml2AuthnResponse = new Saml2AuthnResponse(HttpContext.RequestServices.GetRequiredService<IOptions<Saml2Settings>>().Value); saml2AuthnResponse.ReadXmlFromString(token); try { saml2AuthnResponse.Validate(); if (saml2AuthnResponse.IsValid) { var claimsPrincipal = saml2AuthnResponse.ClaimsPrincipal; return Ok(claimsPrincipal.Claims.Select(c => new { c.Type, c.Value })); } return BadRequest("令牌验证失败"); } catch (Exception ex) { return BadRequest($"验证异常:{ex.Message}"); } }
注意事项
- 确保使用的ADFS签名证书正确:需从ADFS控制台导出签名证书的公钥(.cer格式),无需私钥。
- 调试时开启
ShowPII可获取更详细的异常信息,生产环境必须关闭。 - 若使用方案1仍有问题,可尝试设置
cryptoProviderFactory.AllowUnsafeHashAlgorithms = true(仅临时测试,生产环境不建议)。
内容的提问来源于stack exchange,提问作者planedonut
相关产品推荐
相关产品推荐

