You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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兼容性更好:

  1. 安装NuGet包:
Install-Package ITfoxtec.Identity.Saml2.AspNetCore
  1. 在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");
});
  1. 在控制器中验证令牌:
[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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:45:54