-
Notifications
You must be signed in to change notification settings - Fork 1
WCF outside of IIS with REST JSON, SSL, and Client Certificates
For one of the projects that uses Alien Force, we wanted to make a Windows Service that hosted a WCF component (i.e. this is meant to run on machines that may not have IIS or a proper IIS) and also used REST rather than a more formal protocol like SOAP just to make it much easier to call and evolve over time.
So our simple interface will be as follows:
[ServiceContract]
public interface IEchoServer
{
/// <summary>
/// Just respond with message to verify connectivity
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
[OperationContract]
[WebGet(UriTemplate = "Echo?message={message}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
string Echo(string message);
}
public class EchoServer : IEchoServer
{
public string Echo(string message)
{
return message;
}
}
Regardless of whether you put this in a console app or a service, these simple lines are what hosts the service:
mServerWCF = new ServiceHost(typeof(EchoServer));
mServerWCF.Credentials.ClientCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.Custom;
mServerWCF.Credentials.ClientCertificate.Authentication.CustomCertificateValidator = new Auth();
mServerWCF.Open();
I chose to do the certificate validation stuff in code rather than config because it just felt simpler. My config is as follows:
<?xml version="1.0"?><br/><configuration><br/> <system.serviceModel><br/> <services><br/> <service name="MyServiceNamespace.EchoServer" behaviorConfiguration="EchoServiceBehavior"><br/> <host><br/> <baseAddresses><br/> <add baseAddress="https://someservername/EchoServer"/><br/> </baseAddresses><br/> </host><br/> <!-- this endpoint is exposed at the base address provided by the host--><br/> <endpoint address="" binding="webHttpBinding" behaviorConfiguration="EchoServiceEPBehavior" bindingConfiguration="EchoBinding" contract="MyServiceNamespace.IEchoServer"/><br/> </service><br/> </services><br/><br/> <bindings><br/> <webHttpBinding><br/> <binding name="EchoBinding"><br/> <security mode="Transport"><br/> <transport clientCredentialType="Certificate"/><br/> </security><br/> </binding><br/> </webHttpBinding><br/> </bindings><br/><br/> <!--For debugging purposes set the includeExceptionDetailInFaults attribute to true--><br/> <behaviors><br/> <endpointBehaviors><br/> <behavior name="EchoServiceEPBehavior"><br/> <webHttp helpEnabled="false"/><br/> </behavior><br/> </endpointBehaviors><br/> <serviceBehaviors><br/> <behavior name="EchoServiceBehavior"><br/> <serviceDebug includeExceptionDetailInFaults="True"/><br/> </behavior><br/> </serviceBehaviors><br/> </behaviors><br/><br/> </system.serviceModel><br/><br/><startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration><br/>
I tried many ways to get this to work with “half-arsed” certificate infrastructure. Don’t do this, it’s just painful and I never did get it to work. Instead, get something like Simple Authority and setup a Certificate Authority and make a certificate for the server and client. On the server, install the CA certificate (using just the .cer file) into trusted certificates for the account your hosted service will run as and also install the full certificate (p12) for the server.
So now hopefully you have it working from the command line. We needed to call this from inside IIS, which presented one last challenge. You would likely import the certificate and trusted root into the local machine store to make life easier, but IIS probably doesn’t have permissions to use the private key for the client cert. You’ll probably get “Could not create SSL/TLS secure channel” or error 0×8009030D. To solve this, go into the certificate manager MMC snap-in, right click “Manage Private Keys” and add the user your IIS application is running as to the permissions for the key. If you’re not sure, try granting Everyone rights first, see if that fixes it, and then hone down on the right user (there may be an event log telling you which one it was trying).