Monthly Archives: October 2010

Adding custom attributes to SAML Assertion

In many cases, you need to add custom attributes to a SAML response object and send it to an IdP or an SP. By accessing the attribute list using the Attributes property of the AttributeStatement class, you can easily pass your custom data to the IdP or SP. The following code illustrates how to add some custom attributes including Email, First Name, and Last Name of a user.

C#:

AttributeStatement attributeStatement = new AttributeStatement();
attributeStatement.Attributes.Add(new ComponentPro.Saml2.Attribute("email", SamlAttributeNameFormat.Basic, null,
                                                                             "john@test.com"));
attributeStatement.Attributes.Add(new ComponentPro.Saml2.Attribute("FirstName", SamlAttributeNameFormat.Basic, null,
                                                                             "John"));
attributeStatement.Attributes.Add(new ComponentPro.Saml2.Attribute("LastName", SamlAttributeNameFormat.Basic, null,
                                                                             "Smith"));

// Insert a custom token key to the SAML response.
attributeStatement.Attributes.Add(new ComponentPro.Saml2.Attribute("CustomTokenForVerification", SamlAttributeNameFormat.Basic, null,
                                                                             "YourEncryptedTokenHere"));

samlAssertion.Statements.Add(attributeStatement);

VB.NET:

Dim attributeStatement As New AttributeStatement()
attributeStatement.Attributes.Add(New ComponentPro.Saml2.Attribute("email", SamlAttributeNameFormat.Basic, Nothing, "john@test.com"))
attributeStatement.Attributes.Add(New ComponentPro.Saml2.Attribute("FirstName", SamlAttributeNameFormat.Basic, Nothing, "John"))
attributeStatement.Attributes.Add(New ComponentPro.Saml2.Attribute("LastName", SamlAttributeNameFormat.Basic, Nothing, "Smith"))

' Insert a custom token key to the SAML response.
attributeStatement.Attributes.Add(New ComponentPro.Saml2.Attribute("CustomTokenForVerification", SamlAttributeNameFormat.Basic, Nothing, "YourEncryptedTokenHere"))

samlAssertion.Statements.Add(attributeStatement)

You can add this code to Service.aspx.cs file in Saml2IdpInitiated.IdentityProvider folder. The code after adding custom attributes should look like the following:

C#:

//#define ENCRYPTEDSAML

using System;
using System.Web.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.Web;
using ComponentPro.Saml2;

namespace SamlIdPInitiated.IdentityProvider
{
    public partial class Service : System.Web.UI.Page
    {
        // Get consumer service URL from the application settings.
        private static readonly string ConsumerServiceUrl = WebConfigurationManager.AppSettings["ConsumerServiceUrl"];

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            try
            {
                // Extract the SP target url.
                string targetUrl = Request.QueryString["spUrl"];

                // Validate it.
                if (string.IsNullOrEmpty(targetUrl))
                {
                    return;
                }

                // Create a SAML response object.
                ComponentPro.Saml2.Response samlResponse = new ComponentPro.Saml2.Response();
                // Assign the consumer service url.
                samlResponse.Destination = ConsumerServiceUrl;
                Issuer issuer = new Issuer(GetAbsoluteUrl("~/"));
                samlResponse.Issuer = issuer;
                samlResponse.Status = new Status(SamlPrimaryStatusCode.Success, null);

                Assertion samlAssertion = new Assertion();
                samlAssertion.Issuer = issuer;

                // Use the local user's local identity.
                Subject subject = new Subject(new NameId(User.Identity.Name));
                SubjectConfirmation subjectConfirmation = new SubjectConfirmation(SamlSubjectConfirmationMethod.Bearer);
                SubjectConfirmationData subjectConfirmationData = new SubjectConfirmationData();
                subjectConfirmationData.Recipient = ConsumerServiceUrl;
                subjectConfirmation.SubjectConfirmationData = subjectConfirmationData;
                subject.SubjectConfirmations.Add(subjectConfirmation);
                samlAssertion.Subject = subject;

                // Create a new authentication statement.
                AuthnStatement authnStatement = new AuthnStatement();
                authnStatement.AuthnContext = new AuthnContext();
                authnStatement.AuthnContext.AuthnContextClassRef = new AuthnContextClassRef(SamlAuthenticationContext.Password);
                samlAssertion.Statements.Add(authnStatement);

                // If you need to add custom attributes, uncomment the following code
                // #region Custom Attributes
                // AttributeStatement attributeStatement = new AttributeStatement();
                // attributeStatement.Attributes.Add(new ComponentPro.Saml2.Attribute("email", SamlAttributeNameFormat.Basic, null,
                                                                                             // "john@test.com"));
                // attributeStatement.Attributes.Add(new ComponentPro.Saml2.Attribute("FirstName", SamlAttributeNameFormat.Basic, null,
                                                                                             // "John"));
                // attributeStatement.Attributes.Add(new ComponentPro.Saml2.Attribute("LastName", SamlAttributeNameFormat.Basic, null,
                                                                                             // "Smith"));

                // // Insert a custom token key to the SAML response.
                // attributeStatement.Attributes.Add(new ComponentPro.Saml2.Attribute("CustomTokenForVerification", SamlAttributeNameFormat.Basic, null,
                                                                                             // "YourEncryptedTokenHere"));

                // samlAssertion.Statements.Add(attributeStatement);
                // #endregion

                // Define ENCRYPTEDSAML preprocessor flag if you wish to encrypt the SAML response.
#if ENCRYPTEDSAML
                // Load the certificate for the encryption.
                // Please make sure the file is in the root directory.
                X509Certificate2 encryptingCert = new X509Certificate2(Path.Combine(HttpRuntime.AppDomainAppPath, "EncryptionX509Certificate.cer"), "password");

                // Create an encrypted SAML assertion from the SAML assertion we have created.
                EncryptedAssertion encryptedSamlAssertion = new EncryptedAssertion(samlAssertion, encryptingCert, new System.Security.Cryptography.Xml.EncryptionMethod(SamlKeyAlgorithm.TripleDesCbc));

                // Add encrypted assertion to the SAML response object.
                samlResponse.Assertions.Add(encryptedSamlAssertion);
#else
                // Add assertion to the SAML response object.
                samlResponse.Assertions.Add(samlAssertion);
#endif

                // Get the previously loaded certificate.
                X509Certificate2 x509Certificate = (X509Certificate2)Application[Global.CertKeyName];

                // Sign the SAML response with the certificate.
                samlResponse.Sign(x509Certificate);

                // Send the SAML response to the service provider.
                samlResponse.SendHttpPost(Response.OutputStream, ConsumerServiceUrl, targetUrl);
            }

            catch (Exception exception)
            {
                Trace.Write("IdentityProvider", "An Error occurred", exception);
            }
        }

        private string GetAbsoluteUrl(string relativeUrl)
        {
            Uri u = new Uri(Request.Url, ResolveUrl(relativeUrl));
            return u.ToString();
        }
    }
}

Click here to download the Ultimate SAML SSO Component for ASP.NET.

Encrypting and Decrypting SAML Response XML

This topic illustrates how to encrypt a SAML Response XML on the Identity website and decrypt the XML on the Service Provider website.

Encrypting a SAML Response XML:

Instead of adding an unencrypted SAML Assertion to the SAML response with

// Add assertion to the SAML response object.
samlResponse.Assertions.Add(samlAssertion);

, we need to create an EncryptedAssertion object from the unencrypted Assertion object and add the EncryptedAssertion object to the SAML response object as shown in the code below:

// Load the certificate for the encryption.
// Please make sure the file is in the root directory.
X509Certificate2 encryptingCert = new X509Certificate2(Path.Combine(HttpRuntime.AppDomainAppPath, "EncryptionX509Certificate.cer"), "password");
// Create an encrypted SAML assertion from the SAML assertion we have created.
EncryptedAssertion encryptedSamlAssertion = new EncryptedAssertion(samlAssertion, encryptingCert, new System.Security.Cryptography.Xml.EncryptionMethod(SamlKeyAlgorithm.TripleDesCbc));
// Add encrypted assertion to the SAML response object.
samlResponse.Assertions.Add(encryptedSamlAssertion);

Decrypting the SAML Response XML:

In order to read the encrypted SAML response from the IdP on the Service Provider website, you need to decrypt it and convert to an Assertion object. The following code demonstrates how to do so:

if (samlResponse.GetEncryptedAssertions().Count > 0)
{
    EncryptedAssertion encryptedAssertion = samlResponse.GetEncryptedAssertions()[0];

    // Load the private key.
    // Consider caching the loaded key in production environment for better performance.
    X509Certificate2 decryptionKey = new X509Certificate2(Path.Combine(HttpRuntime.AppDomainAppPath, "EncryptionKey.pfx"), "password");

    // Decrypt the encrypted assertion.
    samlAssertion = encryptedAssertion.Decrypt(decryptionKey.PrivateKey, null);
}
else
{
    throw new ApplicationException("No encrypted assertions found in the SAML response");
}

How about decrypting encrypted attributes?

Very simple. All you need to do is to load a private key file for decrypting attributes and call the Decrypt method of the EncryptedAttribute class. The following code demonstrates how to do so.

// Load the SAML response from the XML document.
Response samlResponse = new Response(xmlDocument.DocumentElement);

// Access the first assertion object.
Assertion assertion = (Assertion)samlResponse.Assertions[0];

if (assertion.AttributeStatements[0].EncryptedAttributes.Count > 0)
{
    // Load the private key file.
    X509Certificate2 certificate = new X509Certificate2(privateCertificateFile, "password");

    // Loop through the encrypted attributes list.
    foreach (EncryptedAttribute encryptedAttribute in assertion.AttributeStatements[0].EncryptedAttributes)
    {
        // Get the encrypted key.
        EncryptedKey encryptedKey = encryptedAttribute.GetEncryptedKeyObjects()[0];

        // Decrypt the encrypted attribute.
        ComponentPro.Saml2.Attribute decryptedAttribute = encryptedAttribute.Decrypt(certificate.PrivateKey, encryptedKey, null);

        // ...
    }
}
else
{
    // Loop through the encrypted attributes list.
    foreach (ComponentPro.Saml2.Attribute attribute in assertion.AttributeStatements[0].Attributes)
    {
        // TO DO: Your code here.

        // ...
    }
}

Click here to download the Ultimate SAML SSO Component for ASP.NET.