WSTrust Bindings for ADFS 2.0 in .NET 4.5

Tags: .NET Framework Security

In .NET Framework 4.5 Microsoft integrated the Windows Identity Foundation into the core framework. A lot of breaking changes comes with this. Read more about the changes in this blog post by Dominick Baier. One of the changes is that Microsoft did not integrate the very useful WS-Trust bindings into the core framework. I often use this bindings to request a security token from a Active Directory Federation Services 2.0 STS (ADFS 2.0). This means in .NET 4.5 you have to set up the binding on your own. I wrote a little helper class which does all this for you.

Here it is:

public static class WSTrust13Bindings
{
    /// <summary>
    /// Binding to talk to kerberosmixed endpoint
    /// </summary>
    public static Binding KerberosMixed
    {
        get { return CreateKerberosBinding(SecurityMode.TransportWithMessageCredential); }
    }
 
    /// <summary>
    /// Binding to talk to username endpoint
    /// </summary>
    public static Binding Username
    {
        get { return CreateUserNameBinding(SecurityMode.Message); }
    }
 
    /// <summary>
    /// Binding to talk to usernamebasictransport endpoint
    /// </summary>
    public static Binding UsernameBasicTransport
    {
        get { return CreateUserNameBinding(SecurityMode.Transport); }
    }
 
    /// <summary>
    /// Binding to talk to usernamemixed endpoint
    /// </summary>
    public static Binding UsernameMixed
    {
        get { return CreateUserNameBinding(SecurityMode.TransportWithMessageCredential); }
    }
 
    /// <summary>
    /// Creates a username binding with the specified security mode.
    /// </summary>
    private static Binding CreateUserNameBinding(SecurityMode securityMode)
    {
        if (securityMode == SecurityMode.None)
            throw new ArgumentException("securityMode None is not allowed");
 
        BindingElementCollection bindingElements = new BindingElementCollection();
 
        // Add securtiy binding element
        if (securityMode == SecurityMode.Message)
            bindingElements.Add(
                SecurityBindingElement.CreateUserNameForCertificateBindingElement());
        else if (securityMode == SecurityMode.TransportWithMessageCredential)
            bindingElements.Add(
                SecurityBindingElement.CreateUserNameOverTransportBindingElement());
 
        // Add encoding binding element
        bindingElements.Add(CreateEncodingBindingElement());
 
        // Add transport binding element
        HttpTransportBindingElement transportBindingElement = 
            CreateTransportBindingElement(securityMode);
 
        if (securityMode == SecurityMode.Transport)
            transportBindingElement.AuthenticationScheme = AuthenticationSchemes.Basic;
        else
            transportBindingElement.AuthenticationScheme = AuthenticationSchemes.Digest;
 
        bindingElements.Add(transportBindingElement);
 
        // Create binding
        return new CustomBinding(bindingElements);
    }
 
    /// <summary>
    /// Creates a kerberos binding with the specified security mode.
    /// On ADFS 2.0 only TransportWithMessageCredential is available.
    /// </summary>
    private static Binding CreateKerberosBinding(SecurityMode securityMode)
    {
        if (securityMode == SecurityMode.None)
            throw new ArgumentException("securityMode None is not allowed");
 
        BindingElementCollection bindingElements = new BindingElementCollection();
 
        // Add securtiy binding element
        if (securityMode == SecurityMode.Message)
            bindingElements.Add(
                SecurityBindingElement.CreateKerberosBindingElement());
        else if (securityMode == SecurityMode.TransportWithMessageCredential)
            bindingElements.Add(
                SecurityBindingElement.CreateKerberosOverTransportBindingElement());
 
        // Add encoding binding element
        bindingElements.Add(CreateEncodingBindingElement());
 
        // Add transport binding element
        HttpTransportBindingElement transportBindingElement = 
            CreateTransportBindingElement(securityMode);
 
        transportBindingElement.AuthenticationScheme = AuthenticationSchemes.Negotiate;
        bindingElements.Add(transportBindingElement);
 
        // Create binding
        return new CustomBinding(bindingElements);
    }
 
    private static HttpTransportBindingElement CreateTransportBindingElement(SecurityMode securityMode)
    {
        // Create transport binding element
        if (securityMode == SecurityMode.Message)
            return new HttpTransportBindingElement();
        else
            return new HttpsTransportBindingElement();
    }
 
    private static TextMessageEncodingBindingElement CreateEncodingBindingElement()
    {
        // Create encoding binding element
        TextMessageEncodingBindingElement encodingBindingElement = 
            new TextMessageEncodingBindingElement();
 
        encodingBindingElement.ReaderQuotas.MaxArrayLength = 2097152;
        encodingBindingElement.ReaderQuotas.MaxStringContentLength = 2097152;
 
        return encodingBindingElement;
    }
}

In my implementation I only support the username and kerberos endpoints. But you can add support for the windows endpoints and the various other endpoints the same way. With this class you can now use the WSTrustChannelFactory the same way as in Windows Identity Foundation. For example:

WSTrustChannelFactory factory = new WSTrustChannelFactory(
  WSTrust13Bindings.UsernameMixed,
  new EndpointAddress("https://<your ADFS server>/adfs/services/trust/13/usernamemixed"));
 
factory.TrustVersion = TrustVersion.WSTrust13;
 
RequestSecurityToken rst = new RequestSecurityToken
{
    RequestType = RequestTypes.Issue,
    KeyType = KeyTypes.Bearer,
    AppliesTo = new EndpointReference("<your realm>")
};
 
factory.Credentials.UserName.UserName = "username";
factory.Credentials.UserName.Password = "password";
 
SecurityToken issuedToken = factory.CreateChannel().Issue(rst);

Hope it helps!

No Comments

Add a Comment