Here is a short step by step guide on how to get your WCF service to perform Message and Transport level security over SSL with user name and password. I ran into this recently and thought should document it along with source code to provide reference for the rest of us.
1. If your development machine is XP (or 2K3 server) and you need dev SSL cert installed on it, follow the instructions mentioned in the articles here. The SelfSSL makes it real easy to do self signed certificates, literally one statement.
Setting up SSL with a SelfSSL certificate on Windows Server 2003 (and XP)
Create a self-signed SSL certificate with IIS 6.0 Resource Kit SelfSSL
2. Create a WCF Service Project. Name the service and contracts appropriately. In my sample it is a simple contract like follows.
[ServiceContract]
public interface IWcfService
{
[OperationContract]
string GetData(int value);
}
Make sure you make the appropriate config changes matching with your service contract.
2. Add a custom validator class in your service. You can create a separate file for it. In this example I have added it to the main service file WcfService.svc.cs. You are going to need to add the reference (not just adding these lines at the top, go to add-reference and add the corresponding dll's to the project)
using System.IdentityModel.Selectors; using System.IdentityModel.Tokens;
and the custom validator code.
public class CustomValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (userName == "test" && password == "test")
return;
throw new SecurityTokenException(
"Unknown Username or Password");
}
}
You probably want to make this user name and password moved to a more secure location or point to your database/authentication store for security and maintainability perspective.
3. Now the code part is done. Move to config file. Enable custom errors so you know details about the errors happening.
<customErrors mode="Off" defaultRedirect="GenericErrorPage.htm">
4. Add a new bindings attribute in the config called SafeServiceConf which will specify the TransportWithMessageCredential type of security. You can add this right before </system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="SafeServiceConf" maxReceivedMessageSize="65536">
<readerQuotas maxStringContentLength="65536" maxArrayLength="65536"
maxBytesPerRead="65536" />
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<bindings> <wsHttpBinding> <binding name="SafeServiceConf" maxReceivedMessageSize="65536"> <readerQuotas maxStringContentLength="65536" maxArrayLength="65536" maxBytesPerRead="65536" /> <security mode="TransportWithMessageCredential"> <message clientCredentialType="UserName" /> </security> </binding> </wsHttpBinding> </bindings>
5. Modify your end point address to refer to this binding configuration
<endpoint address="" binding="wsHttpBinding" contract="MySamples.IWcfService" bindingConfiguration="SafeServiceConf">
also modify your metadata exchange endpoint to use mexHttpsBinding
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>
6. Modify your service behavior to look like this
<behavior name="WcfService.Service1Behavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="MySamples.CustomValidator,WcfService"
/>
</serviceCredentials>
</behavior>
It's recommended that "Include exception in faults" should be disabled when moved to production.
7. Now you are almost ready to run the service however before you do this, make sure that you are running it in the IIS AND you have the SSL enabled on the server as specified in step 1 otherwise you'll run into WCF error stating that there is no HTTPS endpoint available.
You should be able to run and see the service end point as follows.
8. Now that the service is done, let's move towards building the client. Add the service reference to the service end point. You can do it either via entering the entire URL or using the discover feature.
9. Name your reference "Client" or modify your code appropriately. Following is the code for client implementation.
private static void Main(string[] args)
{
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(
delegate { return true; });
var client = new WcfServiceClient();
GetCredentials();
client.ClientCredentials.UserName.UserName = username;
client.ClientCredentials.UserName.Password = password;
Console.Write(client.GetData(1));
client.Close();
Console.Read();
}
The RemoteCertificateValidationCallback part is used to programatically avoid the following warning which would popup due to self signed cert usage.
10. Now run the program.
You can see that for the right credentials, service will run just fine. Otherwise a security exception will be thrown.
Source code can be downloaded from here.WCFAuthSample
Feel free to drop me an email or comment here if you have any questions.
References and Further Readings:
How to: Authenticate with a User Name and Password
WCF Service over HTTPS with custom username and password validator in IIS
Chapter 5 – Authentication, Authorization and Identities in WCF
How to: Use Transport Security and Message Credentials
WCF: Could not establish trust relationship for the SSL/TLS secure channel with authority
Deploying an Internet Information Services-Hosted WCF Service
How messages are encrypted when security mode is "Message"?
Windows HTTP Services Certificate Configuration Tool (WinHttpCertCfg.exe)
Setting up SSL with a SelfSSL certificate on Windows Server 2003 (and XP)
Create a self-signed SSL certificate with IIS 6.0 Resource Kit SelfSSL