Let’s face it, we as developers would love nothing more than to work with the latest and greatest in terms of technology, languages, and frameworks. But, more often then naught, we are called upon to interface with, extend, or gasp rewrite existing applications written in god knows what language. Projects like these are always a sticky situation with developers, and most of the time, end up being a hacked together unmaintainable mess.
I ran into this scenario a couple months ago. My objective was to extend a very old application to send asynchronous requests to a brand new WCF service from a previous project. Since this service required Kerberos authentication, SOAP 1.2, and the like. I opted for a WCF service client proxy DLL to do the heavy lifting since the legacy code language could not handle those requirements.
Creating a Service Channel instance is one of the first things you learn when working with WCF, the challenge here was utilizing Kerberos security.
In a nutshell, there are 3 steps that are required to build the correct Kerberos channel.
/// <summary> /// Creates a Kerberos client Channel used to call the web service endpoint. /// </summary> /// <typeparam name="T">Contract Interface of the service</typeparam> /// <param name="endpointAddress">Endpoint URL of the service</param> /// <param name="spnIdentity">SPN for Kerberos Auth</param> /// <returns>created channel</returns> private T GenerateKerberosClient<T>(string endpointAddress, string spnIdentity) { var kerberosBinding = new WSHttpBinding(); kerberosBinding.Security.Mode = SecurityMode.Message; //set transport security kerberosBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows; kerberosBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None; //set message security kerberosBinding.Security.Message.ClientCredentialType = MessageCredentialType.Windows; kerberosBinding.Security.Message.EstablishSecurityContext =false; kerberosBinding.Security.Message.NegotiateServiceCredential = false; kerberosBinding.Security.Message.AlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128; //create endpoint with spn identity var endpointAddr = new EndpointAddress( new Uri(endpointAddress), EndpointIdentity.CreateSpnIdentity(spnIdentity)); //configure channel factory with transport/message/endpoint configuration var factory = new ChannelFactory<T>(kerberosBinding, endpointAddr); //need to impersonate service account of the service. if (factory.Credentials != null) factory.Credentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation; //return create client channel return factory.CreateChannel(); }
Now obviously, you might not be required to use Kerberos security on your service call. If you don’t, you can retrofit the method to use basicHttpBinding easily.
private bool SendTestServiceRequest(FooBarRequest requestMessage, string endpoint, string spnIdentity) { var client = GenerateKerberosClient<IFooBarService>(endpoint, spnIdentity); return client.SendFooBarRequest(requestMessage); }
You might be wondering why I did not use a configuration file for the configuration. Well, one setback that I ran into while developing this for invocation via another application was .NET couldn’t retrieve the serviceModel configuration via the app.config when invoking the service. Since the app context does not exist, you will need to retrieve and instantiate the configuration manually. I go over the retrieval of a configuration file in .
I’ll post later on about how to create and use a custom channel factory to instantiate your service client code via an app.config.