如何使用C#签名SAMLResponse和加密断言? [英] How to sign SAMLResponse and encrypt assertion with C#?

查看:93
本文介绍了如何使用C#签名SAMLResponse和加密断言?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个IDP,该IDP生成带有签名断言的SAMLResponse.我将不添加生成的XML文件的示例,因为它将使问题变得太长.但是,如果真的有用,请告诉我,然后我将其添加.

I have an IDP that generates a SAMLResponse with a signed assertion. I'm not going to add an example of the generated XML file because it will make the question too long. But if that would be really helpful, let me know, and I'll add it.

SP要求对断言进行加密并对响应进行签名,当前情况并非如此.经过研究,我找不到方法,并且发布了一些我尝试过的代码,但是老实说,我一点都不知道,我尝试过的一切都无济于事.

An SP is requiring that the assertion be encrypted and response signed, which, currently is not the case. After research, I couldn't find how to do it, and I'd post some code that I tried, but I'm a little clueless to be honest, and everything I tried got nowhere.

问题是,如何签名响应并加密断言?

Question is, how do I sign the response and encrypt the assertion?

以下是创建和签名响应的方式:

Here's how the response is being created and signed:

public class SAML
{
    private const int tokenLifetime = 30;
    private const string issuer = "https://some.domain/IdP";
    private const string CertificateSerialNumber = "XXXXXXXXXXXXX";

    private static string _RequestId;
    private static string _RequestIssueInstant;
    private static string _RequestProviderName;
    private static string _RequestACS;
    private static Dictionary<string, string> _claimDescriptors = new Dictionary<string, string>();


    public static string CreateSamlResponse(string RequestId, string RequestIssueInstant, string RequestProviderName, string RequestACS, Dictionary<string,string> claimDescriptors)
    {
        _RequestId = RequestId;
        _RequestIssueInstant = RequestIssueInstant;
        _RequestProviderName = RequestProviderName;
        _RequestACS = RequestACS;
        _claimDescriptors = claimDescriptors;

        var claims = CreateClaims();
        var tokenHandler = new Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler();
        var token = CreateToken(claims, tokenHandler);

        return CreateSamlResponseXml(tokenHandler, token);
    }

    private static Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken CreateToken(IEnumerable<Microsoft.IdentityModel.Claims.Claim> claims,
        Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler tokenHandler)
    {
        var descriptor = CreateTokenDescriptor(claims);
        var token = tokenHandler.CreateToken(descriptor) as Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken;

        AddAuthenticationStatement(token);
        AddConfirmationData(token);

        return token;
    }

    private static void AddConfirmationData(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken token)
    {
        var confirmationData = new Microsoft.IdentityModel.Tokens.Saml2.Saml2SubjectConfirmationData
        {
            Recipient = new Uri(_RequestACS),
            NotOnOrAfter = DateTime.UtcNow.AddSeconds(tokenLifetime),
            InResponseTo = new Microsoft.IdentityModel.Tokens.Saml2.Saml2Id(_RequestId),
        };

        token.Assertion.Subject.SubjectConfirmations.Add(new Microsoft.IdentityModel.Tokens.Saml2.Saml2SubjectConfirmation(
            Microsoft.IdentityModel.Tokens.Saml2.Saml2Constants.ConfirmationMethods.Bearer, confirmationData));
    }

    private static void AddAuthenticationStatement(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken token)
    {
        var authenticationMethod = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password";
        var authenticationContext = new Microsoft.IdentityModel.Tokens.Saml2.Saml2AuthenticationContext(new Uri(authenticationMethod));
        var authenticationStatement = new Microsoft.IdentityModel.Tokens.Saml2.Saml2AuthenticationStatement(authenticationContext);
        token.Assertion.Statements.Add(authenticationStatement);
    }

    private static string CreateSamlResponseXml(Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler tokenHandler, Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken token)
    {
        var buffer = new StringBuilder();

        using (var stringWriter = new StringWriter(buffer))
        using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings()))
        {
            xmlWriter.WriteStartElement("Response", "urn:oasis:names:tc:SAML:2.0:protocol");
            xmlWriter.WriteAttributeString("IssueInstant", DateTime.UtcNow.ToString("o"));
            xmlWriter.WriteAttributeString("ID", "_" + Guid.NewGuid());
            xmlWriter.WriteAttributeString("Version", "2.0");

            xmlWriter.WriteStartElement("Status");
            xmlWriter.WriteStartElement("StatusCode");
            xmlWriter.WriteAttributeString("Value", "urn:oasis:names:tc:SAML:2.0:status:Success");
            xmlWriter.WriteEndElement();
            xmlWriter.WriteEndElement();

            tokenHandler.WriteToken(xmlWriter, token);

            xmlWriter.WriteEndElement();
        }
        return buffer.ToString();
    }

    private static Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor CreateTokenDescriptor(IEnumerable<Microsoft.IdentityModel.Claims.Claim> claims)
    {
        var descriptor = new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor()
        {
            TokenType = Microsoft.IdentityModel.Tokens.SecurityTokenTypes.OasisWssSaml2TokenProfile11,
            Lifetime = new Microsoft.IdentityModel.Protocols.WSTrust.Lifetime(DateTime.UtcNow, DateTime.UtcNow.AddMinutes(1)),
            //AppliesToAddress = appliesTo,
            AppliesToAddress = _RequestACS,
            TokenIssuerName = issuer,
            Subject = new Microsoft.IdentityModel.Claims.ClaimsIdentity(claims),
            SigningCredentials = GetSigningCredentials()
        };

        return descriptor;
    }

    private static System.IdentityModel.Tokens.SigningCredentials GetSigningCredentials()
    {    
        System.Security.Cryptography.X509Certificates.X509Certificate2 myCertificate = null;

        X509Certificate2Collection selectedCerts = new X509Certificate2Collection();

        X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadOnly);

        foreach (System.Security.Cryptography.X509Certificates.X509Certificate2 cert in store.Certificates)
        {
            if (cert.SerialNumber.Trim().ToLower().Equals(CertificateSerialNumber.ToLower())){ myCertificate = cert; }
        }
        return new Microsoft.IdentityModel.SecurityTokenService.X509SigningCredentials(myCertificate, System.IdentityModel.Tokens.SecurityAlgorithms.RsaSha1Signature, System.IdentityModel.Tokens.SecurityAlgorithms.Sha1Digest);
    }

    private static IEnumerable<Microsoft.IdentityModel.Claims.Claim> CreateClaims()
    {
        foreach (var claimDescriptor in _claimDescriptors)
        {
            yield return new Microsoft.IdentityModel.Claims.Claim(claimDescriptor.Key, claimDescriptor.Value);
        }
    }

}

推荐答案

除SigningCredentials属性外,SecurityTokenDescriptor和Saml2SecurityToken类还具有EncryptingCredentials属性.设置它,断言将被加密.

The SecurityTokenDescriptor and Saml2SecurityToken classes, in addition to SigningCredentials property, have an EncryptingCredentials property. Set it and the assertion will be encrypted.

签名响应可能会比较棘手.获得具有加密断言的响应xml之后,可以使用 https://github.com/Safewhere/CHTestSigningService/blob/86a66950d1ffa5208b8bf80d03868a073ba29f12/Kombit.Samples.CHTestSigningService/Code/TokenSigningService.cs#L344

Signing response might be trickier. After you have the response xml with encrypted assertion, you can use the SignedXml class for that purpose. One example is https://github.com/Safewhere/CHTestSigningService/blob/86a66950d1ffa5208b8bf80d03868a073ba29f12/Kombit.Samples.CHTestSigningService/Code/TokenSigningService.cs#L344

这篇关于如何使用C#签名SAMLResponse和加密断言?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆