Wednesday, January 1, 2014

Secure a WCF REST Service with an X509 Certificate, hosted on IIS

Sometimes, we want to expose some API (services) publicly. In order to secure the service, we can go with standard user name/password authentication. We can also add extra security by validating client IP. Services can also be secured by enabling X509 Certificate authentication if it is meant for closed user group or few partners. There are many other options to secured the services.

In this article, we will discuss how to enable X509 authentication in WCF 
RESTFull service that is hosted on IIS.

Here are the steps:
  1. Create certificates
  2. Configure the web.config
  3. Configure the IIS
  4. Sample client application

1. Create certificates

Ideally the certificates should be obtained from the trusted certificate authority. But in the development environment, we can create it using makecert.exe that comes with Visual Studio. 

We need one certificate for the server and one certificate for each unique client. For this example, we will create two certificates one for the server and one for the client. Visit here to know more about makecert.exe.

makecert -sv srvr.pvk srvr.cer -n CN=testServer
makecert -sv client1.pvk client1.cer -n CN=testClient1

Generate Pfx file from pvk file. Visit here for more.

pvk2pfx -pvk srvr.pvk -pi password -spc srvr.cer  -pfx srvr.pfx
pvk2pfx -pvk client1.pvk -pi password -spc client1.cer  -pfx client1.pfx

Once we have both the certificate files ready, we have to register them.

In the server:
Import srvr.pfx into LocalMachine -> Personal, Certificates 
Import client1.cer into LocalMachine -> Trusted People, Certificates

In the client
Import client1.pfx into LocalMachine -> Personal, Certificates 

We might have to import both the files in the systems' LocalMachine -> Trusted Root Certificate Authorities. This is not required in case of valid certificates.

2.  Configure the web.config

Once the certificates are ready, we have to configure the WCF service to set security mode is Transport and client credential type is Certificate as mentioned below:
<system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="webSecureBinding">
          <security mode="Transport">
            <transport clientCredentialType="Certificate"/>
          </security>
        </binding>
      </webHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="securedBehavior">
          <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="webBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <services>
      <service name="RestServerWcf.Service" behaviorConfiguration="securedBehavior">
        <endpoint address="" binding="webHttpBinding" bindingConfiguration="webSecureBinding" behaviorConfiguration="webBehavior" contract="RestServerWcf.IService"/>
      </service>
    </services>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>


3. Configure the IIS

We also have to configure IIS for SSL (https). First lets add new binding for https. Open the IIS, select the web site and click on Bindings at right hand side:

Add new binding:

Configure SSL Settings, click on SSL Settings:
And finally set the Require SSL:


4. Sample client application

Once, we are done with all the configuration then lets build sample client application to consume the services
// Generate request
HttpWebRequest request = WebRequest.Create(@"https://server/service.svc/json/customers") as HttpWebRequest;

// Find the certificate from the local store
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly);
X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindBySubjectName, "Client", false);
X509Certificate cer1 = new X509Certificate(collection[0]);

// Add the certificate into the request            
request.ClientCertificates.Add(cer1);

// Hit the service and get the response
HttpWebResponse response = (HttpWebResponse)request.GetResponse();

Done.