WCF Transport Security With Certificate Authentication — Test Validation With MSTest V2
In this article, we demonstrate the use of X.509 certificates for server and client authentication when using WCF transport security.
Join the DZone community and get the full member experience.
Join For FreeThis 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 enterprise applications inside 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 [1].
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 [1].
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 those 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 [8]).
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 [8]. The certificate message security level, along with message encryption, allows both the client and the service to get authentication with a certificate.
Configuring the Service
Our service defines the repeatedString
operation that should return an integer representing the number of occurrences of a
in the prefix length of Lili lowercase English letters that were repeated infinitely many times.
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 of 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 character occurrences. With performance in mind, 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 config file. The following web.config shows how to configure the WSHttpBinding to use transport security and X.509 client credentials [1]. To mimic a large binding, we define the timeout connection in sending, and receive, as well as in open and close attributes. Also, we define the maximum amount of memory allocated in bytes for the buffer manager [2] in maxBufferPoolSize and for a message that can be received on a channel [3] maxRecievedMessageSize. At the 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 [4].
...
<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 is for metadata exchange<endpoint address="mex"
that exposes 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 [4].
With the behavior and binding defined posteriorly, 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>
...
MSTest V2 Parameterized Test Client
The new Visual Studio MSTest V2 allows us to use parameters in the tests 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 in[DataRow("ababbaa", 10, 6)
. The first parameter is a string to be repeated, the second is the number of characters to consider, and the last one is the expected result.
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 calling the client.RepeatedString(repeatedString, number)
. 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 the 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 [5].
...
<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
For client behavior, we add a < clientCredentials
> element to the < behaviors
> element [6]. Bear in mind that it 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 findValueto appropriate values certificate information [6].
...
<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 at 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 thebindingConfiguration
attribute to the binding for the client [6].
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 a Unit Test, and Download the Source Code
- To run the unit test, open Test Explorer by choosing Test > Windows > Test Explorer from the top menu bar [7].
- 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 [7].
The source code is hosted on Git Hub. To download it, please follow this link.
References:
Published at DZone with permission of Jailson Evora. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments