WS-Federation Token Encryption using Microsoft Katana

Scott Brady
Scott Brady
Katana

When using the WS-Federation protocol, you usually (or at least should) use certificates to sign your token, allowing the receiver to verify the contents have not been altered in transit, and for Transport Layer Security (TLS, think SSL) in order to provide privacy for network communications.

What is less common but also useful is SAML assertion encryption. This token encryption is useful when your SAML token includes claims/assertions that contain private data which might be held for a long period of time or passed around through untrusted intermediaries.

This certificate has its public key held by the Security Token Service (STS) in order to encrypt the token, and its private key held by the Relying Party in order to decrypt it.

This process is relatively well documented if you are dealing with Windows Identity Foundation (WIF) 1.0 and slightly less so with WIF 4.5, however there are currently little to no resources on how to achieve this with the latest OWIN/Katana components.

SecurityTokenHandlers

To understand incoming tokens, Katana components use a collection of SecurityTokenHandlers. A SecurityTokenHandler is configured for a specific token type and is responsible for reading and writing that token type. Out of the box, the Ws-Fed OWIN Katana component initialises three of these for the following token types: JWT, SAML 1.1 and SAML 2.0.

System.IdentityModel vs Microsoft.IdentityModel

A quick note on namespaces. When WIF was still separate from .NET (referred to as WIF 1.0 or 3.5) SecurityTokenHandlers were found in the Microsoft.IdentityModel namespace. Since being moved into the .NET framework as of .NET 4.5, Microsoft now recommends you use the classes in the System.IdentityModel namespace.

However! With the release of Katana, we are now back in the Microsoft.IdentityModel namespace for SAML related token handlers thanks to the new package Microsoft.IdentityModel.Protocol.Extensions that comes as a dependency of the WS-Federation OWIN component.

Under the covers these SAML security token handlers still use the token handlers from System.IdentityModel, however they do not inherit from them; instead they only implement the methods in the interface ISecurityTokenValidator, found in the System.IdentityModel.Tokens.Jwt package, and use the existing handlers to handle the logic.

The Ws-Federation Katana component only uses the methods defined in ISecurityTokenValidator.

WS-Federation Katana Component

Now for some code. First we’ll start with a Client using WS-Federation for authentication. We'll need the following nuget packages:

Install-Package Microsoft.Owin.Security.Cookies
Install-Package Microsoft.Owin.Security.WsFederation

This will pull down the required dependencies, including the aforementioned System.IdentityModel.Tokens.Jwt and Microsoft.IdentityModel.Protocol.Extensions packages. Don't forget to also pull down your OWIN host of choice (e.g Microsoft.Owin.Host.SystemWeb for running in IIS).

Now we need our OWIN startup class configured to use cookies as our local sign in type and Ws-Federation as our method of communicating with our STS.

app.UseCookieAuthentication(new CookieAuthenticationOptions {AuthenticationType = "cookies"});

app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions { Wtrealm = "urn:encryptedrealm", MetadataAddress = "https://localhost:44311/wsfed/metadata", Wreply = "https://localhost:44422/", SignInAsAuthenticationType = "cookies" });

Normally with this configuration we can quite happily log in to our STS, but when we receive an encrypted token we are met with:

SAML 2.0

System.IdentityModel.Tokens.EncryptedTokenDecryptionFailedException: ID4022: The key needed to decrypt the encrypted security token could not be resolved. Ensure that the SecurityTokenResolver is populated with the required key.

SAML 1.1

SecurityTokenValidationException: IDX10201: None of the SecurityTokenHandlers could read the 'securityToken'.

To resolve this we need to load in the private key that can decrypt the token.

SAML 2.0 Token Encryption

Token Encryption for SAML 2.0 is the simplest to set up as it works out of the box with the WS-Federation Katana Component by using the TokenValidationParameters property of WsFederationAuthenticationOptions.

TokenValidationParameters = new TokenValidationParameters {
    AuthenticationType = "cookies",
    ClientDecryptionTokens = 
      new ReadOnlyCollection<SecurityToken>(new List<SecurityToken> { 
        new X509SecurityToken(Cert.LoadEncrypting())
      })
}

Where Cert.LoadEncrypting returns the our private key as a X509Certificate2. This can be done by loading from the certificate store, but for the sake of this demonstration I am loading from an embedded resource.

This is all that is required to decrypt a SAML 2.0 token using the WS-Federation Katana Component!

Source Code

You can find a working copy of this SAML 2.0 token encryption on GitHub using IdentityServer3 as the STS.

SAML 1.1 Token Encryption

When you encrypt a token using the SAML 1.1 handler, you actually create a token of type EncryptedSecurityToken. An EncryptedSecurityToken is a completely different token type and therefore needs its own token handler. This is currently only provided in the form of System.IdentityModel.Tokens.EncryptedSecurityTokenHandler. Internally this handler will decrypt the incoming token and then pass it back to the parent SecurityTokenHandlerCollection. Unfortunately these internals do not use TokenValidationParameters, nor do they use methods defined in ISecurityTokenValidator that the Katana components rely upon.

All of this means that there is a whole lot of extra work required to deal with SAML 1.1 encrypted tokens.

Solution

Luckily there is already a blog post on this by Chris Klug detailing how to implement an EncryptedSecurityTokenHandler and SamlSecurityTokenHandler that the WS-Federation Katana component can work with. Note that this post cuts a couple of corners to get the decryption working and doesn't use the TokenValidationParameters, so maybe don't use it verbatum, however it is a very good place to start.

Future

I'd be interested to hear if anyone has a huge requirement for SAML 1.1 token encryption and for it to work with OWIN. As far as I'm aware no one has implemented/released a solution to this yet and it may be something I could dedicate some time to.