Friday, 18 September 2009

Injecting service certificates into a WCF end point using Windsor’s WcfIntegration

I recently hit an issue whilst using Windsor’s WCF Integration Facility (see previous post on configuring the the facility) to create a WCF end point. Previously the WCF integration was working fine but I had to change the binding to a wsHttpBinding and use a mutual certificate to secure the message. Normally you would setup the endpoint binding to load the required certificate out of the machines certificate store, my problem was that the certificate was not  in the machines local store and needed to be loaded of disk.

The first thing to do is configure what you can in the configuration file, in my case it was setting up the wsHttpBinding.
<ws2007HttpBinding>
    <binding name="MyCustomServiceBinding">
        <security mode="Message">
            <message clientCredentialType="Certificate" negotiateServiceCredential="false" establishSecurityContext="false" />
        </security>
    </binding>
</ws2007HttpBinding>
This configures the service to use a certificate client credential type but if we were to run the service now we would get an Exception saying that no Service certificate has been specified. This is where we need to write some code and have Windsor inject it into the service host factory. To do this we need to create an end point behaviour using the IEndpointBehavior interface defined in the System.ServiceModel assembly. The method we are interested in is the AddBindingParameters method which allows us to add custom data which is added to the binding at runtime. One point to note that you should not manipulate the endpoint directly, if you do the execution behaviour is undefined.
public class MyCustomEndPointBehavior : IEndpointBehavior
{
    public void Validate(ServiceEndpoint endpoint) {}
 
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        var serviceCredentials = bindingParameters.Remove<ServiceCredentials>();
        serviceCredentials = serviceCredentials == null ? new ServiceCredentials() : serviceCredentials.Clone();
        serviceCredentials.ServiceCertificate.Certificate = new X509Certificate2(@"C:\FilePath\Cert.pfx","CertPassword",X509KeyStorageFlags.MachineKeySet);
        bindingParameters.Add(serviceCredentials);
    }
 
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) {}
 
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) {}
}
What we do is look to see if there is a ServiceCredentials object in the binding parameter collection creating one if there is not and add our certificate as the ServiceCertificate. It is worth noting that this code will be run for all end points so if you have more than one you will need to use something to identify the end points you are interested such as the end points contract type.

Finally we need to wire the end point behaviour into Windsor, this is as simple as registering the type.
var myCustomEndpointBehavior = new MyCustomEndPointBehavior();
 
container.AddFacility<WcfFacility>().Register(
    Component.For<IEndpointBehavior>().Instance(myCustomEndpointBehavior),
    Component.For<IMyCustomService>().ImplementedBy<MyCustomService>());
 
DefaultServiceHostFactory.RegisterContainer(container.Kernel);
You are not limited to using the IEndpointBehavior, WCF also provides the IServiceBehavior interface which interacts at the service level (what’s in a name?). This works on the same principle but gives you access to the service host base class object but the same principles apply and again modifying the host directly will have an undefined execution behaviour.

No comments:

Post a Comment