Introduction
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 note 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
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.
Next add the Certificates MMC Snap-in and select Computer account.
Navigate to the “Personal” node to see ClientCertificate
and ServerCertificate
.
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.
Click Add and select HTTPS and then Select ServerCertificate
that you have created earlier.
Okay done. Everything is okay now.
Service and its Configuration
Here, I have used a 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:
="1.0"="UTF-8"
<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 have been used.
The custom behavior section is given below:
<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>
Here, clientCertificate describes an X.509 certificate which has been used to validate a client to a service. At the same time, serviceCertificate specifies an X.509 certificate that will be used to authenticate the service to clients. There is a detailed explanation about that has been given here [^] [^].
And the custom binding configuration section is:
<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>
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:
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 my own validation logic to check for it.
So the configuration file for the client is:
="1.0"="utf-8"
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="MyCustomBinding">
<security authenticationMode="CertificateOverTransport"/>
<httpsTransport />
</binding>
</customBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="MyCustomBehavior">
<clientCredentials>
<clientCertificate findValue="ClientCertificate"
storeLocation="LocalMachine" storeName="My"
x509FindType="FindBySubjectName" />
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="https://localhost/Feedback/Feedback.svc"
binding="customBinding" bindingConfiguration="MyCustomBinding"
contract="Rashim.RND.WCF.SecureCommunication.Interfaces.IFeedback"
behaviorConfiguration="MyCustomBehavior" name="FeedbackEndpoint">
<identity>
<dns value="ServerCertificate"/>
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
That’s it! I think this would help you while you will be working with the Certificate based communication.
Filed under: C#, Codeproject, Technical, WCF
Tagged: C#, HTTPS, SSL Certificate, WCF