In this post I will explain how you can build an Windows Communication Foundation web service and client which use a Username and Password combination to authenticate a user. The most difficult action is to create a X509 certificate which is used to encrypt messages passed back and forward to the server.

In this application we will use WCF’s wsHttpBinding and message level security provided by an X509 certificate. The X509 certificate encryption is required by WCF because the client credentials (username/password) are passed as clear text in the SOAP message.

There is one problem that we will face during this series of posts. WCF is reluctant to accept a test certificate, it requires a lot of extra work to get it done. However once you understand the steps that you need to take, you will find it an repetitive but easy task.

I hope you find this post useful. If you have any questions or comments, feel free to post them as reactions on this post. Enjoy!

Generating a Certificate

The first step is to create a test X509 certificate, which is used to encrypt the messages. The certificate will be placed in the ‘My’ folder on the ‘Local Machine’ store under the name ‘MyServerCert’.

To generate the necessary certificate, execute the following command in the windows SDK command line utility:

makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=MyServerCert -sky exchange –pe

Warning: This certificate should be used for testing purposes only.

Setting Up the Service

There are several steps we need to perform on the service, to force it to use username/password validation.

The first step is to implement the validator class which takes the username/password combination and ensures they are correct. First make sure you reference the System.IdentityModel assembly, next create a new class and derive it from System.IdentityModel.Selectors.UserNamePasswordValidator. Now override the Validate function derived from UserNamePasswordValidator.

Now you can write code that checks if the username/password combination is valid, if the combination is not valid throw a System.IdentityModel.Tokens.SecurityTokenException.

Here is my implementation:

public class UsernameValidator: UserNamePasswordValidator
{
public override void Validate(string userName, string password)
	{
		// validate arguments
		if (string.IsNullOrEmpty(userName))
			throw new ArgumentNullException("userName");
		if (string.IsNullOrEmpty(password))
			throw new ArgumentNullException("password");

		// check if the user is not test
		if (userName != "test" || password != "test")
			throw new SecurityTokenException("Unknown username or password");
	}
}

The next step is to configure the service to use our custom validator and enforce the username/password client credentials.

First create a new binding with the following configuration:

<bindings>
<wshttpbinding>
		<binding name="mySecureBinding">
			<security mode="Message">
				<message clientCredentialType="UserName" />
			</security>
		</binding>
	</wshttpbinding>
</bindings>

Notice that the binding enforces message level security and the client has to provide the UserName credentials.

Now that the binding is ready we need to define some behaviour for the service endpoint:

<behaviors>
<servicebehaviors>
		<behavior name="defaultProfile">
			<servicecredentials>
				<servicecertificate findValue="MyServerCert" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My" />
				<usernameauthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Premotion.Services.UsernameValidator, App_Code" />
			</servicecredentials>
		</behavior>
	</servicebehaviors>
</behaviors>

There are two interesting elements in this section: serviceCertificate and userNameAuthentication. The first specifies the certificate which the service uses to encrypt and decrypt the messages. The second element specifies our custom validator.

Now make sure all your endpoints use the correct binding and behaviour. That’s all for the server!

Modifying the Client

Modifying the client could have been an easy task, if we had accesses to a valid certificate. WCF will not accept the test certificate without a bunch of tricks. First I will pretend the service certificate is valid, then I will explain how to get WCF to accept the test certificate.

The first step is to create a new binding in the application configuration:

<bindings>
<wshttpbinding>
		<binding name="mySecureBinding">
			<security mode="Message">
				<message clientCredentialType="UserName" />
			</security>
		</binding>
	</wshttpbinding>
</bindings>

This exactly the same binding as we configured the service with.

The next is to set the username/password credentials in code. The following code should be placed next to the code which instantiates the service client:

base.ClientCredentials.UserName.UserName	= "test";
base.ClientCredentials.UserName.Password	= "test";

That is all. If you have a valid certificate you can run the code, if you have the test certificate perform the steps below to get the code running.

Bypass Certificate Validation

First make sure you reference the System.IdentityModel assembly. The first step is to create a class which validates the certificates and derive it from System.IdentityModel.Selectors.X509CertificateValidator. Override the Validate method. You can leave the implementation empty if you want all the certificates to pass. If you detect a wrong certificate you can throw a System.IdentityModel.Tokens.SecurityTokenValidationException.

Here is my implementation:

public class MyX509Validator: X509CertificateValidator
{
	public override void Validate(X509Certificate2 certificate)
	{
		// validate argument
		if (certificate == null)
			throw new ArgumentNullException("certificate");

		// check if the name of the certifcate matches
		if (certificate.SubjectName.Name != "CN=MyServerCert")
			throw new SecurityTokenValidationException("Certificated was not issued by thrusted issuer");
	}
}

The next step is to create a new endpoint behaviour, which tells WCF to use our custom certificate validator:

<behaviors>
<endpointbehaviors>
		<behavior name="myClientBehavior">
			<clientcredentials>
				<servicecertificate>
					<authentication certificateValidationMode="Custom" customCertificateValidatorType="Premotion.Services.MyX509Validator,client" />
				</servicecertificate>
			</clientcredentials>
		</behavior>
	</endpointbehaviors>
</behaviors>

The last step is to set the DNS identity for the endpoint:

<client>
	<endpoint address="http://localhost:1494/services/coreservice.svc" binding="wsHttpBinding" bindingConfiguration="mySecureBinding" contract="Premotion.Services.ICoreServiceClientContact" name="CoreService" behaviorConfiguration="myClientBehavior">
		<identity>
			<dns value="MyServerCert"/>
		</identity>
	</endpoint>
</client>

Now everything is finished and you can execute the code.

You can download the sample by clicking here, make sure you create the certificate manually! I hope you gained some insight on how WCF works with username/password authentication. Thank you for reading, if you have any questions or comments post them.