WCF Transport Security with Certificate Authentication

This post demonstrates the use of X.509 certificates for server and client authentication when using WCF transport security. This security mode is ideal for communication between applications in the intranet network.

Our demo creates a service that will implement a simple, repeated string algorithm. In this scenario, the service is hosted under Internet Information Services (IIS) which is configured with Secure Sockets Layer (SSL) and configured with an SSL certificate to allow clients to verify the identity of the server.

On the other hand, our client is a parameterized test project that will validate the logic of our service. The client is also configured with an X.509 certificate that allows the service to verify the identity of the client. The server’s certificate must be trusted by the client, and the client’s certificate must be trusted by the server in a request/reply message pattern as illustrated by the following diagram.

We choose transport as the security mode, allowing the communication between existing client and service. Data Integrity and Data Confidentiality are preserved. The transport protocol is HTTPS with WSHttpBinding binding.

WCF Transport Security – Transfer Security Mode and Message Security Level

WCF offers diverse transfer security modes and message security level to ensure a secured communication between a client and a server. One of them is Transport. It is the easiest way to achieve a secured transfer of messages via the use of communication protocols such as TCP, IPC, Https, and MSMQ. This mode is more effective when the transfer is point-to-point and is used mostly in a controlled environment, i.e., intranet applications.

The message security level is not dependent on WCF protocols. It is employed with message data itself by encrypting the data by using a standard algorithm. Certificate message security level along with message encryption allows both the client and the service to get authentication with a certificate.

WCF Transport Security – Configuring the Service

Our service defines one method from Lili lowercase English letters that she repeated infinitely many times. It should return an integer representing the number of occurrences of a in the prefix of length in the infinitely repeating string.

Interface

The interface defines RepeatedString as the only method/operation. The operation allows the insertion of two parameters, one of type string as a string that repeats infinitely many times and an integer as the number of characters to consider in counting the repeated character. The operation will return an object with the count of the letter a’s in the first number letters of the infinite repeated string.

...
namespace RepeatedString
{
    [ServiceContract]
    public interface IRepeatedString
    {
        [OperationContract]
        long RepeatedString(string repeatedString, long number);
    }
}

Implementation

The operation interacts through the repeated string, counting the number of a character occurrence. With performance in mind, firstly we should calculate the factor and the remainder of the repeated string, avoiding interact through all characters.

...
namespace RepeatedString
{
    public class RepeatedString : IRepeatedString
    {
        long IRepeatedString.RepeatedString(string repeatedString, long number)
        {
            long count_repeated_a = 0,  factor = number / repeatedString.Length, remainder = number % repeatedString.Length;

            for (int i = 0; i < repeatedString.Length; i++)
            {
                if (repeatedString[i] == 'a') count_repeated_a += (i < remainder) ? factor + 1 : factor;
            }
            return count_repeated_a;
        }
    }
}

Binding

Since the service in this scenario is hosted under IIS, it is configured with a web.config file. The following web.config shows how to configure the WSHttpBinding to use transport security and X.509 client credentials. To mimic a large binding, we define the timeout connection with sending, receive, as well in open and close tag attributes. Also, we define the maximum amount of memory allocated, in bytes, for the buffer manager in maxBufferPoolSize and for a message that can be received on a channel maxReceivedMessageSize. At security tag, we define a transport mode with clientCredentialType="Certificate" highlighted in line 21 of the code.

...
<bindings>
  <wsHttpBinding>
    <binding name="wsHttpBinding_LargeBinding"
      closeTimeout="00:01:00"
      openTimeout="00:01:00"
      receiveTimeout="00:10:00"
      sendTimeout="00:10:00"
      bypassProxyOnLocal="false"
      transactionFlow="false"
      hostNameComparisonMode="StrongWildcard"
      maxBufferPoolSize="250000000"
      maxReceivedMessageSize="250000000"
      messageEncoding="Text"
      textEncoding="utf-8"
      useDefaultWebProxy="true"
      allowCookies="false">
      <readerQuotas maxDepth="2000000" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
      <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
      <security mode="Transport">
        <transport clientCredentialType="Certificate" />
      </security>
    </binding>
  </wsHttpBinding>    
</bindings>
...

Behavior

At service behaviors, we disable includeExceptionDetailInFaults to avoid disclosure of sensitive information. During development, you may want your service to also send other exceptions back to the client to assist you in debugging. This is a development-only feature and should not be employed in deployed services.

...
<behaviors>
  <serviceBehaviors>
    <behavior name="DebugModeBehavior">
      <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true"/>
      <serviceDebug includeExceptionDetailInFaults="false"/>
    </behavior>
  </serviceBehaviors>
</behaviors>
...

Services

Finally, at the services tag, we define our service, exposing two endpoints. One for metadata exchange [<endpoint address="mex" ...] that allow exposes metadata about the service. It is useful to publish metadata about the service like Web Services Description Language (WSDL) document that describes all of the methods and data types employed by a service. Returning metadata about a WCF service allows consumers of service to easily create clients for the service.

With the behavior and binding defined, we complete the attributes for our main endpoint [<endpoint address="endpointRepeatedStringService" binding="wsHttpBinding"].

...
<services>
  <service behaviorConfiguration="DebugModeBehavior" name="RepeatedString.RepeatedString">
    <endpoint address="endpointRepeatedStringService" binding="wsHttpBinding" name="EndpointRepeatedString" bindingConfiguration="wsHttpBinding_LargeBinding" contract="RepeatedString.IRepeatedString" />
    <endpoint address="mex" binding="wsHttpBinding" bindingConfiguration="wsHttpBinding_LargeBinding" name="mexEndpoint" contract="IMetadataExchange" />
  </service>
</services>
...

SSL Settings

After we configure our service, we need to enable Secure Socket Layer (SSL) on IIS to secure the service. Start the IIS Manager, in connections panel, under sites, select the ReapeatedStringservice (1) that we want to enable SSL.

Image title

On the selected service > Home page in the central panel, double-click SSL Settings (2).

Image title

In SSL Settings page, first, select Require SSL checkbox and under Client certificates, select Require. To persist, under Actions panel on the right, apply these changes.

Image title

WCF Transport Security – Parameterized Test Client

The new Visual Studio MSTest V2 allows us to use parameters in the test class. This class could contain multiple test methods and each method is executed with the different parameters provided. It helps save time when executing the same tests with different inputs and expected results, as described below[DataRow("ababbaa", 10, 6)]. The first parameter is a string to repeat, the second is the number of characters to consider and the last one is the expected result.

WCF Transport Security Test class implementation

The test project generates the WSDL document of our service describes as RepeatedStringReference. In the arrange, we instantiate the client object and we act calling client.RepeatedString(repeatedString, number) operation. To validate, we assert that the expected results have occurred.

...
namespace UnitTest_TransportSecurity_W_CertificateAuth
{
    [TestClass]
    public class RepeatedString_UnitTest
    {
        [DataRow("ababbaa", 10, 6)]
        [DataRow("aaaaa", 123, 123)]
        [DataRow("ccc", 1000000000000, 0)]
        [DataRow("a", 1000000000000, 1000000000000)]
        [DataTestMethod]
        public void RepeatedStringTest(string repeatedString, long number, long expected)
        {
            RepeatedStringReference.RepeatedStringClient client = new RepeatedStringReference.RepeatedStringClient();
            
            Assert.AreEqual(client.RepeatedString(repeatedString, number), expected);

            client.Close();
        }
    }
}

Client Binding

The client can be configured problematically or in an app.config file. In this config, the client binding does not differ from service configuration binding. Both have the same timeout config as well as other attributes. Another important defined tag is XmlDictionaryReaderQuotas. It contains configurable quota values for XML dictionary readers which limit the amount of memory utilized by an encoder while creating a message.

...
<bindings>
  <wsHttpBinding>
    <binding name="wsHttpBinding_LargeBinding"
      closeTimeout="00:01:00"
      openTimeout="00:01:00"
      receiveTimeout="00:10:00"
      sendTimeout="00:10:00"
      bypassProxyOnLocal="false"
      transactionFlow="false"
      hostNameComparisonMode="StrongWildcard"
      maxBufferPoolSize="250000000"
      maxReceivedMessageSize="250000000"
      messageEncoding="Text"
      textEncoding="utf-8"
      useDefaultWebProxy="true"
      allowCookies="false">
      <readerQuotas maxDepth="2000000" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
      <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
      <security mode="Transport">
        <transport clientCredentialType="Certificate" />
      </security>
    </binding>
  </wsHttpBinding>    
</bindings>
...

Client Behavior

At client behavior, we add a <clientCredentials> element to the <behaviors> element. Bear in mind that is crucial to set the required name attribute to an appropriate value. We also add a <clientCertificate> element to the <clientCredentials> element setting these attributes storeLocation, storeName, x509FindType, and findValue to appropriate values certificate information.

...
<behaviors>
  <endpointBehaviors>
    <behavior name="endpointCredentialBehavior">
      <clientCredentials>
        <clientCertificate findValue="Certificate Subject Name"
                            storeLocation="CurrentUser"
                            storeName="My"
                            x509FindType="FindBySubjectName" />
      </clientCredentials>
    </behavior>
  </endpointBehaviors>
</behaviors>
...

Client Endpoint

When configuring the client, we should specify the behavior by setting the behaviorConfiguration attribute on the <endpoint> element. The endpoint element is a child of the <client> element. Also, we need to specify the name of the binding configuration by setting the bindingConfiguration attribute to the binding for the client. If we generated the configuration file, the binding’s name is automatically generated.

...
<client>
  <endpoint name="EndpointRepeatedString" address="https://localhost/RepeatedString/RepeatedString.svc/endpointRepeatedStringService" behaviorConfiguration="endpointCredentialBehavior" binding="wsHttpBinding" bindingConfiguration="wsHttpBinding_LargeBinding" contract="RepeatedStringReference.IRepeatedString" />
</client>
...

Run Unit test and download the source-code

WCF-Transport-Security-with-Certificate-Authentication test explorar tab

1 – To run the unit test open Test Explorer by choosing Test > Windows > Test Explorer from the top menu bar.

2 – Run your unit tests by clicking Run All.

After the tests have completed, a green checkmark indicates that a test passed. A red “x” icon indicates that a test failed.

https://github.com/JERBEvora/WCF_Transport_Security_With_Cert_Post

The source code is hosted on Git Hub. To download it, please follow the link attached to the git icon.

Leave a Reply

Your email address will not be published. Required fields are marked *