Rashim's Blog

Archive for March 2013


When working with distributed application, securing communication between the client and the service is a very vital issue. Windows Communication Foundation provides the facility of transfer security which is responsible for ensuring the integrity and confidentiality of service messages, and also responsible for providing authentication. Transfer security in WCF is achieved through the use of either transport security or message security. Transport-layer security provides integrity and confidentiality, while message-layer security provides a variety of passes which are not possible with transport security mechanisms. When using transport security, user credentials are transport-dependent, which allows fewer authentication options compared to message security. And each transport protocol has its own way for passing credentials and handling message guard. But SSL is the most common approach for encrypting and signing the contents sent over HTTPS. Here I will explain how to configure WCF Services and Clients to communicate over HTTPS by using self-signed Certificates.

When I intend to write any technical stuff, my intention always goes to start with a very simple example as well as gives an overview with the necessary things related to it. There is no difference this time as well. So for the purposes of this blog post I am going to organize it with Self-Signed Certificate Creation, Services and its Configuration, Clients and its Configuration. Please be noted that I would like to use custom binding for the code examples given here.

Self-Signed Certificate Creation

In Certificate based communication, we need to use two certificates for the client and server to authenticate each other. To make those certificates I would like to use the “MakeCert.exe” utility with the following arguments

makecert -pe -ss My -sr LocalMachine -a sha1 -sky exchange -n CN=ServerCertificate

makecert -pe -ss MY -sr LocalMachine -a sha1 -sky exchange -n CN=ClientCerttificate

Image

The complete specification of this is available here

Now,

First bring up the Microsoft Management Console by launching mmc.exe which allows us to browse the local machine’s cache of certificates.

Image

Next add the Certificates MMC Snap-in and select Computer account

Image

Navigate to the “Personal” node to see ClientCertificate and ServerCertificate.

Image

It is also required to export the certificates from the certificate store and import the copies of those into the TrustedPeople store so that WCF can find them for validation purposes.

Since I would like to use IIS hosting, we need to configure IIS for https. To do that,

  • Open IIS Manager
  • Select Default Web site and open its property window
  • Click the Edit Binding.

Image

Click Add and select HTTPS and then Select ServerCertificate that you have created earlier

Image

Okay done .Everything is okay now.

Service and its Configuration

I have used here very simple service which is “Feedback” service. The service contract looks like,

[ServiceContract]
public interface IFeedback
{
    [OperationContract]
    string GiveFeedback(string question);
}

With such a simple contract the implementation is just straight forward

public class Feedback : IFeedback
{
    public string GiveFeedback(string question)
    {
        var feedback = question + " : This is very funny stuff";
        return feedback;
    }
}

The configuration file for this Service is given below,

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service
      name="Rashim.RND.WCF.SecureCommunication.WcfService.Feedback"
      behaviorConfiguration="MyCustomBehavior">
        <endpoint address=""
        binding="customBinding"
        contract="Rashim.RND.WCF.SecureCommunication.Interfaces.IFeedback"
        bindingConfiguration="MyCustomBinding"/>
        <endpoint
        address="mex"
        binding="mexHttpsBinding"
        contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyCustomBehavior">
          <serviceMetadata httpsGetEnabled="true" />
          <serviceCredentials>
            <clientCertificate>
              <authentication
              certificateValidationMode="PeerTrust"
              trustedStoreLocation="LocalMachine" />
            </clientCertificate>
            <serviceCertificate
            findValue="ServerCert"
            x509FindType="FindBySubjectName"
            storeLocation="LocalMachine"
            storeName="My"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <customBinding>
        <binding name="MyCustomBinding"
        closeTimeout="00:20:00"
        openTimeout="00:20:00"
        receiveTimeout="00:20:00"
        sendTimeout="00:20:00">
          <security
          authenticationMode="CertificateOverTransport"
          requireSecurityContextCancellation="true"/>
          <httpsTransport/>
        </binding>
      </customBinding>
    </bindings>
    <serviceHostingEnvironment
    multipleSiteBindingsEnabled="false" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="false" />
    <directoryBrowse enabled="true" />
  </system.webServer>
</configuration>

To get a better idea about the different sections of this configuration file I would like to explain it a bit more,

<services>
  <service
  name="Rashim.RND.WCF.SecureCommunication.WcfService.Feedback"
  behaviorConfiguration="MyCustomBehavior">
    <endpoint
    address="" binding="customBinding"
    contract="Rashim.RND.WCF.SecureCommunication.Interfaces.IFeedback"
    bindingConfiguration="MyCustomBinding"/>
    <endpoint address="mex"
    binding="mexHttpsBinding"
    contract="IMetadataExchange" />
  </service>
</services>

In this above section we see that Custom behavior configuration and custom binding configuration has been used.

The custom behavior section is given below,

<behavior name="MyCustomBehavior">
  <serviceMetadata httpsGetEnabled="true" />
  <serviceCredentials>
    <clientCertificate>
      <authentication
      certificateValidationMode="PeerOrChainTrust"
      trustedStoreLocation="LocalMachine" />
    </clientCertificate>
    <serviceCertificate
    findValue="ServerCert"
    x509FindType="FindBySubjectName"
    storeLocation="LocalMachine"
    storeName="My"/>
  </serviceCredentials>
</behavior>

Here clientCertificate describes an X.509 certificate which has been used to validate a client to a service. At the same time, serviceCertificate specify an X.509 certificate that will be used to authenticate the service to clients. There is a detail explanations about this has been given here [^] [^].

And the custom binding configuration section is,

<binding name="MyCustomBinding"
closeTimeout="00:20:00"
openTimeout="00:20:00"
receiveTimeout="00:20:00"
sendTimeout="00:20:00">
  <security
  authenticationMode="CertificateOverTransport"
  requireSecurityContextCancellation="true">
  </security>
  <sslStreamSecurity requireClientCertificate="true"/>
  <httpsTransport/>
</binding>

We know that, WCF provides several modes by which clients and services verify to each other. We can create binding for these authentication modes through configuration or by code. Here I have used CertificateOverTransport which means the service is valid using an X.509 certificate at the transport layer.  And requireSecurityContextCancellation specifies whether security context should be cancelled and terminated when it is no longer required.

That’s it.Now try to  browse https://localhost/Feedback/Feedback.svc and you will get the following page

Image

Client and its Configuration

Once services have been implemented, the implementation of client is pretty simple. The client code has been given below,

System.Net.ServicePointManager.ServerCertificateValidationCallback += (se, cert, chain, sslerror) => true;

        var channel = new ChannelFactory<IFeedback>("FeedbackEndpoint");

        var client = channel.CreateChannel();

        var input = Console.ReadLine();

        while (input != null && input.ToLower() != "exit")
        {
            var feedback = client.GiveFeedback(input);
            Console.WriteLine(feedback);
            input = Console.ReadLine();
        }

The code above will trust any security certificate handed back from the server since it bypasses the SSL certificate validation. As the certificate I have used here is a self-signed certificate and it is not signed by a trusted CA, I need to make own validation logic to check for it .

So the configuration file for the client is,

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="MyCustomBinding">
          <security authenticationMode="CertificateOverTransport"/>
          <textMessageEncoding />
          <httpsTransport />
        </binding>
      </customBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="MyCustomBehavior">
          <clientCredentials>
            <clientCertificate
            findValue="ClientCert"
            storeLocation="LocalMachine"
            storeName="My"
            x509FindType="FindBySubjectName" />
            <serviceCertificate>
              <authentication
              certificateValidationMode="PeerOrChainTrust"/>
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <client>
      <endpoint
      address="https://localhost/FeedbackService/Feedback.svc"
      binding="customBinding"
      bindingConfiguration="MyCustomBinding"
      contract="Rashim.RND.WCF.SecureCommunication.Interfaces.IFeedback"
      behaviorConfiguration="MyCustomBehavior"
      name="FeedbackEndpoint"/>
    </client>
  </system.serviceModel>
</configuration>

That’s it. I think this would help you while you will be working with the Certificate based communication.

Source code is available here.


%d bloggers like this: