Enjoy my free ebook which covers major .NET related topics such as WCF, WPF, WWF, Ajax, Core .NET, SQL Server, Architecture and much more; download from
here
On a broader basis WCF supports two kinds of security, transport level and message level security. Transport means the medium on which WCF data travels while message means the actual data packets sent by WCF.
Transport medium can be protocols like TCP, HTTP, MSMQ etc. These transport mediums by themselves provide security features like HTTP can have SSL security (HTTPS). WCF has the capability of leveraging underlying transport security features on WCF service calls.
Message level security is provided in the data itself using WS-Security. In other words it's independent of the transport protocol. Some examples of message level security are messages encrypted using encryption algorithm, messages encrypted using X509 certificate etc, messages protected using username etc.
WCF gives you an option to either just use message level security in stand alone, transport level in stand alone or a combination of both. If you are interested in how to do message level security and transport security in a standalone manner you can read more from
here.
The best security is the combination of transport and message. In this article we will see step by step how to implement dual security using 'SSL' plus message security using 'Username' using 'WsHttpBinding'.
The first step is to customize your 'Wshttp' binding with proper security mode and credential type. There are three options in security mode 'Transport', 'Message' and 'TransportWithMessageCredential'.
Since we are implementing dual security, we need to use the last one i.e. 'TransportWithMessageCredential' where the transport security is provided by SSL and message security is provided using 'UserName and password'.
The second thing we need to provide is the credential type. There are five different credential types: none, windows, username, certificate and issued token. Credential type defines how the credentials will be passed over the transport layer. For the current instance we will select 'UserName'.
So summing up we will provide security mode as 'TransportWithMessageCredential' and message security will be provided by 'UserName'.
So create a WCF service using the WCF service template and in 'web.config' provide the security mode and credential type as shown in the code snippet shown below.
<bindings><wsHttpBinding><binding name="Binding1">
<!-- UsernameToken over Transport Security --><security mode="TransportWithMessageCredential" >
<message clientCredentialType="UserName"/></security></binding></wsHttpBinding></bindings>
Once we have customized the 'WsHttp' binding with security mode and credential type it's time to create the custom class which will do authentication of the user name provided.
In order to create your custom class you need to inherit the 'UserNamePasswordValidator' class which belongs to 'System.IdentityModels.Selector'.
The code snippet of 'MyValidator' class is shown below. We need to override the 'Validate' method with the authentication logic as shown below.
class MyValidator : UserNamePasswordValidator
{public override void Validate(string userName, string password)
{if ((userName == "shiv123") && (password == "pass123"))
{}
else
{
throw new FaultException("Invalid credentials");
}
}
}
If the credentials are not proper then we have raised the 'FaultException' error which can be caught by the WCF client to display error messages.
So we are almost 50% through now. We have customized the 'WsHttp' binding and created our custom class 'MyValidator' which will do the necessary authentication. The next step is to define behavior.
'Behaviors' define customized run time logic over the binding agreement. Currently we need to execute 'MyValidator' class logic for the 'UserName' provided in the WCF service by WCF client.
To specify the behavior, go to your 'Web.config' file and in the 'servicecredentials' tag specify the 'userNameAuthentication' tag which points to the custom class 'MyValidator'.
<behaviors>
<serviceBehaviors><serviceCredentials><userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyValidator, app_code"/></serviceCredentials>
</behavior>
We have already mentioned transport security will be provided by SSL while message security will be provided by 'username'. We have already configured 'UserName' message security using 'MyValidator' class which is specified in the behavior section of 'web.config' file. The next step is to configure SSL i.e. transport security for our WCF service.
We will be using 'makecert.exe' which is a free tool given by Microsoft to enable HTTPS for testing purpose. MakeCert (Makecert.exe) is a command-line tool that creates an X.509 certificate that is signed by a system test root key or by another specified key. The certificate binds a certificate name to the public part of the key pair. The certificate is saved to a file, a system certificate store, or both.
You can get the same from "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\Bin" or you can also get it from windows SDK.
You can type the commands shown below into a command prompt in "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\Bin". Please note "compaq-jzp37md0" is the server name so you need to replace with your PC name.
makecert -r -pe -n "CN= compaq-jzp37md0 " -b 01/01/2000 -e 01/01/2050 -eku 1.3.6.1.5.5.7.3.1 -ss my -sr
localMachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12
If you run the same through your command prompt you should get a succeeded message as shown below.
Now it's time to assign this certificate to your IIS website. So go to IIS properties, click on directory security tab and you should see server certificate tab.
So click on the server certificate tab and you will then be walked through an IIS certificate wizard. Click 'Assign an existing certificate' from the wizard.
You can see a list of certificates. The "compaq-jzp37md0" certificate is the one which we just created using 'makecert.exe'.
Now try to test the site without 'https' and you will get an error as shown below….That means your certificate is working.
Do not forget to enable IIS anonymous access.
We also need to make couple of changes in the WCF service 'Web.config' 'endpoint' section as shown below. You can see how the address points to HTTPS and binding uses 'mexHttpsBinding'.
<service name="Service" behaviorConfiguration="ServiceBehavior"><endpoint address="https://localhost/Service.svc" binding="wsHttpBinding" contract="IService" bindingConfiguration="Binding1"><endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/></service>
It's time to consume WCF the service application. So click on add service reference and specify your service URL. You will be shown a warning box as shown in the below figure as the certificate is a test certificate. So just let it go.
The next step is to create WCF proxy client object and pass the credentials as shown in the below snippet.
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(IgnoreCertificateErrorHandler);
ServiceReference1.ServiceClient obj = new ServiceReference1.ServiceClient();
obj.ClientCredentials.UserName.UserName = "shiv123";
obj.ClientCredentials.UserName.Password = "pass123";
Response.Write(obj.GetData(12));
'makecert.exe' creates test certificates. In other words it's not signed by CA. So we need to suppress those errors in our ASP.NET client consumer. So we have created a function called as 'IgnoreCertificateErrorHandler' which return true even if there are errors. This function is attached as a callback to 'ServicePointManager.ServerCertificateValidationCallback' as shown in the above code snippet.
public static bool IgnoreCertificateErrorHandler(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
If everything goes appropriate you should be able to run the WCF service. Try changing the use rid and password to something else you should get the fault exception message provided in the 'MyValidtor' class.
Source code
You can get the above source from top of this article.