Separate certificates for Transport and Message security in WCF

I’ve been busy of late writing my first book and doing so many other things that I haven’t had time to post anything on my blog. Now that I’ve got the book out of the way, I thought I should post something here. And what better topic than WCF 🙂

Recently, I had to interact with a financial institution using WCF for a Customer. The Service that the financial institution exposed was not written in WCF or .NET – not that it matters, but there were a number of specific things that had to be done to get it to work:

  • We needed to use transport security (https) that had to be encrypted using a specific X509 certificate
  • The body of the message had to be signed using another X509 certificate
  • The reply from the service did not have any security credentials attached to it – i.e. the transport was secure, but the message was not signed or encrypted

This may seem pretty straight forward – All you had to do is create a custom binding and specify something like this –

<custombinding>
  <binding name="Custom">
    <security messagesecurityversion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10" 
      authenticationmode="MutualCertificate" 
    />
    <httpstransport requireclientcertificate="true" 
      authenticationscheme="Negotiate" 
      usedefaultwebproxy="true" 
      manualaddressing="false" 
    />
  </binding>
</custombinding>

The problem is that you can specify only one certificate in  Client credentials  and both message security as well as transport security will use the same certificate – we want to use two separate ones.

The solution to this is to add a new behavior that takes care of this. But rather than creating the behavior from scratch, an easier alternative is to extend the ClientCredentials class to cater for this additional certificate. So, I decided to use the existing certificate stored in ClientCredentials for message security and to add a separate property to hold the certificate for the Transport as shown in the code below –

 
/// 
/// Class that extends Client Credentials so that the certificate for the
/// Transport layer encryption can be separate
/// 
public class MyCredentials : ClientCredentials
{
  /// <summary>
  /// The X509 Certificate that is to be used for https
  /// </summary>
  public X509Certificate2 TransportCertificate { get; set; }

  public MyCredentials(ClientCredentials existingCredentials)
    : base(existingCredentials)
  {
  }

  protected MyCredentials(MyCredentials other)
    : base(other)
  {
    TransportCertificate = other.TransportCertificate;
  }

  protected override ClientCredentials CloneCore()
  {
    return new MyCredentials(this);
  }

  public override SecurityTokenManager CreateSecurityTokenManager()
  {
    return new MyCredentialsSecurityTokenManager(this);
  }

  public void SetTransportCertificate(string subjectName, StoreLocation storeLocation, StoreName storeName)
  {
    SetTransportCertificate(storeLocation, storeName, X509FindType.FindBySubjectDistinguishedName, subjectName);
  }

  public void SetTransportCertificate(StoreLocation storeLocation, StoreName storeName, X509FindType x509FindType, string subjectName)
  {
    TransportCertificate = FindCertificate(storeLocation, storeName, x509FindType, subjectName);
  }

  private static X509Certificate2 FindCertificate(StoreLocation location, StoreName name,
    X509FindType findType, string findValue)
  {
    X509Store store = new X509Store(name, location);
    try
    {
      store.Open(OpenFlags.ReadOnly);
      X509Certificate2Collection col = store.Certificates.Find(findType, findValue, true);
      return col[0]; // return first certificate found
    }
    finally
    {
      store.Close();
    }
  }

}

As part of the class, I added some helper methods to set the Transport certificate from code and also overrode the CreateSecurityTokenManager method so that I can create my own SecurityTokenManager that figures out which certificate to use for what operation.

But again, rather than create this class from scratch, I just extended the ClientCredentialsSecurityTokenManager class that ClientCredentials uses. In it I overrode the CreateSecurityTokenProvider method so that when a certificate is requested for Transport security, we pass back TransportCertificate that is stored in the MyCredentials object as shown in the code below –

 
internal class MyCredentialsSecurityTokenManager :
    ClientCredentialsSecurityTokenManager
{
    MyCredentials credentials;

    public MyCredentialsSecurityTokenManager(MyCredentials credentials)
        : base(credentials)
    {
        this.credentials = credentials;
    }

    public override SecurityTokenProvider CreateSecurityTokenProvider(
        SecurityTokenRequirement requirement)
    {
        SecurityTokenProvider result = null;

        if (requirement.Properties.ContainsKey(ServiceModelSecurityTokenRequirement.TransportSchemeProperty) &&
            requirement.TokenType == SecurityTokenTypes.X509Certificate)
        {
            result = new X509SecurityTokenProvider(
                this.credentials.TransportCertificate);
        }
        else if (requirement.KeyUsage == SecurityKeyUsage.Signature &&
            requirement.TokenType == SecurityTokenTypes.X509Certificate)
        {
            result = new X509SecurityTokenProvider(
                this.credentials.ClientCertificate.Certificate);
        }
        else
        {
            result = base.CreateSecurityTokenProvider(requirement);
        }

        return result;
    }


}

The last step is create the stuff necessary to be able to specify this in your config file. For that I extended the ClientCredentialsElement, so that I can specify the Transport Certificate as a behavior using the code below –

 
class ClientCredentialsExtensionElement : ClientCredentialsElement
{
    ConfigurationPropertyCollection properties;

    public override Type BehaviorType
    {
        get 
        { 
            return typeof(MyCredentials); 
        }
    }

    [ConfigurationProperty("transportCertificate")]
    public X509InitiatorCertificateClientElement TransportCertificate 
    {
        get
        {
            return base["transportCertificate"] 
                as X509InitiatorCertificateClientElement;
        }
    }

    protected override ConfigurationPropertyCollection Properties
    {
        get
        {
            if (this.properties == null)
            {
                ConfigurationPropertyCollection properties = base.Properties;
                properties.Add(new ConfigurationProperty(
                    "transportCertificate",
                    typeof(X509InitiatorCertificateClientElement), 
                    null, null, null, 
                    ConfigurationPropertyOptions.None));
                this.properties = properties;
            }
            return this.properties;
        }
    }

    protected override object CreateBehavior()
    {
        MyCredentials creds = new MyCredentials(
            base.CreateBehavior() as ClientCredentials);

        PropertyInformationCollection properties = 
            ElementInformation.Properties;

        creds.SetTransportCertificate(TransportCertificate.StoreLocation,
                                        TransportCertificate.StoreName, 
                                        TransportCertificate.X509FindType, 
                                        TransportCertificate.FindValue);

        base.ApplyConfiguration(creds);
        return creds;
    }
}

With the changes made, you should be able to replace the clientCredential section in your config file with the clientCredentialsExtension section. Something like this –



<system.serviceModel>
...
 <extensions>
   <behaviorExtensions>
     <add name="clientCredentialsExtension" type="MyNamespace.ClientCredentialsExtensionElement, MyAssemblyName" />
   </behaviorExtensions>

 </extensions>
 <behaviors>
   <endpointBehaviors>
     <behavior name="SecureMessageAndTransportBehavior">

       <clientCredentialsExtension>


         <!--This cert is used for signing the message-->
         <clientCertificate findValue="YourMessageCertName"
                            storeLocation ="LocalMachine"
                            storeName="My"
                            x509FindType="FindBySubjectName"
                            />

         <!--This cert is used for the transport-->
         <transportCertificate findValue="YourTransportCertName"
                            storeLocation ="LocalMachine"
                            storeName="My"
                            x509FindType="FindBySubjectName"
                            />

       </clientCredentialsExtension>

     </behavior>
   </endpointBehaviors>
 </behaviors>

</system.serviceModel>

That’s it – you are all set to go. Just make sure that you set this behavior for your endpoint.

Advertisements

WCF Presentation slides

I’ve given a WCF presentation a number of times and every time some one asks me for the slide deck. And I keep saying that I’ll post it in my blog, but have been guilty of not doing it.

So, finally after a bit of prodding, I decided to post one. This one is called Practical WCF and is part 1 of a series. It gives a fair bit of introduction to WCF and also talks about Service contracts, Data contracts and Fault contracts.

Here is the link to download it: Practical WCF Part 1. ppsx

WCF Presentation

I went to the WCF Presentation given by Juval Lowy at the Victoria .NET Dev SIG today. Juval is here in Australia to conduct WCF Master Class as part of Readify’s Industrial Strength series. I am not trying to market the course, but I believe there are still a few places open for the 5 day course in Sydney which starts next week. The course contents looks really good – Can’t wait to get my hands on the course material from someone who has been to the Melbourne course.

Interestingly, I felt today’s presentation at the Dev SIG was at the right level for the audience that had turned up. I would rate the people who usually turn up to these meetings to be between the intermediate to advanced level as far as .Net skills/experience is concerned. I usually get bored when I turn up to these meetings and the content covered is very basic – I’ve heard the same complaints from a few other people as well. I am also mindful that we shouldn’t shut out beginners from these events by only covering advanced topics. The right mix would be to split these presentations into two parts – cover half an hour of beginner to intermediate topics and then cover more advanced topics in the remaining time.