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 a 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 communication between existing clients and services. 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 levels to ensure secure 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’s 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);
}
}
WCF Transport Security – Implementation
The operation interacts through the repeated string, counting the number of character occurrences. With performance in mind, firstly we should calculate the factor and the remainder of the repeated string, avoiding interaction 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
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 allows exposing metadata about the service. It is useful to publish metadata about the service like a 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 the service to easily create clients for the service.
With the behavior and binding defined, we complete the attributes for our main endpoint [<endpoint address="
...
<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 the connections panel, under sites, select the ReapeatedString
service (1) that we want to enable SSL.
On the selected service > Home page in the central panel, double-click SSL Settings (2).
In the SSL Settings page, first, select Require SSL checkbox and under Client Certificates, select Require. To persist, under th
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
. 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 described as RepeatedStringReference
. In the arrangement, we instantiate the client object and we act client
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 an <clientCredentials>
element to the <behaviors>
element. Bear in mind that it is crucial to set the required name attribute to an appropriate value. We also add an <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
attribute on<client>
element. Also, we need to specify the name of the binding configuration by setting the bindingConfiguration
attribute
...
<client>
<endpoint name="EndpointRepeatedString" address="https://jailsonevora.com/RepeatedString/RepeatedString.svc/endpointRepeatedStringService" behaviorConfiguration="endpointCredentialBehavior" binding="wsHttpBinding" bindingConfiguration="wsHttpBinding_LargeBinding" contract="RepeatedStringReference.IRepeatedString" />
</client>
...
Run the Unit test and download the source-code
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 been completed, a green checkmark indicates that a test passed. A red “x” icon indicates that a test failed.
The source code is hosted on Git Hub. To download it, please follow the link attached to the git icon.