如何使用C#签署SAMLResponse并加密断言?

时间:2017-10-02 16:14:31

标签: c# .net authentication saml saml-2.0

我有一个IDP,它生成带有签名断言的SAMLResponse。我不打算添加生成的XML文件的示例,因为它会使问题太长。但如果那真的有用,请告诉我,我会加上它。

SP要求对断言进行加密并对响应进行签名,但目前情况并非如此。经过研究,我找不到怎么做,我发布了一些我尝试过的代码,但说实话,我有点无能为力,而且我尝试的一切都无处可去。

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

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

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);
        }
    }

}

1 个答案:

答案 0 :(得分:1)

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

签署回复可能会更棘手。使用加密断言获得响应xml后,可以使用SignedXml class来实现此目的。一个例子是https://github.com/Safewhere/CHTestSigningService/blob/86a66950d1ffa5208b8bf80d03868a073ba29f12/Kombit.Samples.CHTestSigningService/Code/TokenSigningService.cs#L344