I have a Windows service that self hosts a WCF service for clients to connect to, current it using unsecure HTTP connection. I have bought an SSL certificate from a trusted CA that I already use to secure my web application successfully.
I want to secure the Windows service so that the host server and clients can install the certificate and only these clients would be authenticated to connect to the WCF service.
As a test I have created a simple Host/Client console application to test the security using the certificate.
Currently the service is showing as secure when I navigate to the baseline address, but when the client attempts to connect to the service and use one the calls, I get an error in the client application stating “The HTTP request was forbidden with client authentication scheme ‘Anonymous’.”
Right now I have the client set up to present the certificate to the WCF service defined in its app.config, so it shouldn’t be trying to connect anonymously. I’ve tried changing the authentication settings in ISS, but obviously that wouldn’t work as the WCF is not hosted in IIS, but as a Windows service and in this test, it is just a console application.
I have the certificate installed on both the server and client “Local Computer – Trusted Root Certification Authorities” and “Local Computer – Personal” stores. I have even tried putting it in the “Trusted People” store.
Below you can see the C# code and the app.configs for the Host/Client.
Host Program.cs (I have changed the actual URL to testendpoint for security reasons):
using System;
using System.ServiceModel;
namespace SimplifiedHostService
{
[ServiceContract]
public interface ISimplifiedHostServiceContract
{
[OperationContract]
string SimplifiedHostServiceMethod(string input);
}
public class SimplifiedHostService : ISimplifiedHostServiceContract
{
public string SimplifiedHostServiceMethod(string input)
{
return "You said: " + input;
}
}
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("https://testendpoint:8000/SimplifiedHostService");
// Create a ServiceHost instance
using (ServiceHost host = new ServiceHost(typeof(SimplifiedHostService), baseAddress))
{
try
{
host.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
host.Close();
}
catch (CommunicationException ex)
{
Console.WriteLine("An exception occurred: {0}", ex.Message);
host.Abort();
}
}
}
}
}
Host app.config:
<configuration>
<system.serviceModel>
<services>
<service name="SimplifiedHostService.SimplifiedHostService">
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="secureBinding"
contract="SimplifiedHostService.ISimplifiedHostServiceContract" />
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="secureBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceCredentials>
<serviceCertificate storeLocation="LocalMachine"
storeName="Root"
x509FindType="FindBySubjectName"
findValue="TestCertificateSubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Client Program.cs
// Shared service contract interface (copied from server project)
using System.ServiceModel;
using System;
namespace SimplifiedHostService
{
[ServiceContract]
public interface ISimplifiedHostServiceContract
{
[OperationContract]
string SimplifiedHostServiceMethod(string input);
}
}
namespace WCFClient
{
class Program
{
static void Main(string[] args)
{
// Create a channel to the service using the shared service contract interface
var factory = new ChannelFactory<SimplifiedHostService.ISimplifiedHostServiceContract>("SimplifiedHostService");
SimplifiedHostService.ISimplifiedHostServiceContract channel = null;
try
{
// Create a channel to the service
channel = factory.CreateChannel();
// Call the service method
string result = channel.SimplifiedHostServiceMethod("Hello from client");
// Display the result
Console.WriteLine("Result from service: " + result);
}
catch (Exception ex)
{
Console.WriteLine("An error occurred: " + ex.Message);
}
finally
{
// Close the channel factory and the channel
if (channel != null && channel is ICommunicationObject)
{
((ICommunicationObject)channel).Close();
}
factory.Close();
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Client app.config:
<configuration>
<system.serviceModel>
<client>
<endpoint name="SimplifiedHostService"
address="https://testendpoint:8000/SimplifiedHostService"
binding="basicHttpBinding"
bindingConfiguration="secureBinding"
behaviorConfiguration="test"
contract="SimplifiedHostService.ISimplifiedHostServiceContract" />
</client>
<bindings>
<basicHttpBinding>
<binding name="secureBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="test">
<clientCredentials>
<!-- Specify the client certificate -->
<clientCertificate storeLocation="LocalMachine"
storeName="Root"
findValue="TestCertificateSubjectName"
x509FindType="FindBySubjectName"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>