WCF Error: The requested service could not be activated

If and when a WCF client receives any one of the following errors…

The requested service, ‘net.tcp://mydomain:808/Services/MyService.svc’ could not be activated. See the server’s diagnostic trace logs for more information.

OR

System.ServiceModel.CommunicationObjectFaultedException: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.

the problem may be due to the .config file of the WCF Host.

Try removing the address from the endpoint that you are having problems with. For example:
The original .config may look something like this (Note the address value of the net.tcp service endpoint).

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="MyService">
        <endpoint address="net.tcp://mydomain:808/Services/MyService.svc" 
                  binding="netTcpBinding" 
                  contract="IMyService"/>
        
      </service>
    </services>
    ....
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

See what happens if you remove the endpoint address like this:

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="MyService">
        <endpoint address="" 
                  binding="netTcpBinding" 
                  contract="IMyService"/>
        
      </service>
    </services>
    ....
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

I have no idea why the removal of the address resolved the error the client receives (It did for me, anyway) – nor can I figure out why explicitly defining the url to the service throws an error. In my little knowledge of WCF, it seems it would be the other way around.

If you can fill me in as to why removing the service’s endpoint address from the hosts .config file can, in certain cases, resolve exception errors, please leave me a message :)

For the record, the errors mentioned above can also be thrown when the router that the WCF Host lives behind has not opened its ports to allow for the Net.Tcp protocol.

Advertisements

WCF Service Using Username/Password Authentication

This example WCF application will contain these features:

  1. Force the client to pass a username and password prior to calling any of the services operations
  2. The service will be exposed over a net.tcp endpoint
  3. The net.tcp binding will implement TransportWithMessageCredentials security
  4. The endpoint behavior will allow httpGet for it’s Metadata
  5. The endpoint behavior will expose an SSL Certificate (used for the TransportWithMessageCredentials)
  6. The endpoint behavior will use a custom MembershipProvider for authenticating the username and password that was sent from the client
  7. The host will be IIS 7
  8. The client will be a Windows Form

The purpose of this WCF example is to show you how to use Visual Studio 2010 and IIS 7 to create the hooks that are necessary for supporting the features that are mentioned in the feature list above. It may be worth mentioning that this example is just one of many different ways to set up the service.

Download the source files here

The WCF Service

Using Visual Studio 2010, create a “WCF Service Library” project.

The default code generated by VS2010 creates two contracts:

  1. [ServiceContract]: Defines the interface of the operations  that will be exposed to the clients
  2. [DataContract]: Defines a custom data type that will be used by the client and service. The data contract can be your own classes that do whatever it is that you want to do – Just make sure you decorate your classes with the [DataContract] and [DataMember] attributes

The service that VS2010 generated is stub code that will be removed/replaced in a real production application. However, the stub code will be sufficient to demonstrate the plumbing that is necessary for client credential authentication between the client and service that we want to demonstrate in this example.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WcfServiceLibrary
{
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);

[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
}

[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";

[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}

[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
}

The WCF Host

This example will use IIS 7 to host the WcfServiceLibrary.Service1 service.

In the same Solution as the WcfServiceLibrary.Service1, add a new web application to IIS by right-clicking the Solution and choosing “Add > New Web Site”.

Specify the Web Location to http://localhost/WcfServiceHost

Add a reference to the web application that points to the WcfServiceLibary project. After you add the reference to our service assembly, you will notice the web application now has a Bin directory that contains the .dll of our service. If you don’t see the Bin directory, run a Build on the web application.

By default, VS2010 will add a couple of files that define a WCF service (IService.cs and Service.cs) and will point to that service in its .svc file. Since we have already created our own service and we have added a reference to our service, we have no need for the auto-generated service – so, let’s remove that service and then update the .svc file so that it points to our service.

Delete the IService1.cs and Service1.cs files that are located in the App_Code directory of the website ((this is that WCF service that VS2010 auto-created when we added the website).

Update the Service.svc file so that it points to the reference to our service assembly (as opposed to the service that was auto-generated when the web application was created).

Old Service.svc content…
<@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>

Updated Service.svc content…
<%@ ServiceHost Service="WcfServiceLibrary.Service1" %>

Notice how the Service.svc file points to the assembly reference WcfServiceLibrary.Service1 (this is the hook for configuring the web application to use the WcfServiceLibrary.Service1.dll that is located in its Bin directory).

Reviewing what we have done so far

At this point we have a project that defines a WCF Service and have created a web application that references that service and will host that service. We now need to configure IIS (our host) to support the bindings, protocols, and security that we want to implement.

Configuring IIS (our WCF Host)

Open IIS Manager…

  1. From the Start menu, click Run.
  2. In the Open box, type inetmgr, then click OK.

Add the Net.Tcp Binding

Right-click on the Default Web Site then choose “Edit Bindings…“. Add a net.tcp binding type and set the Binding Information to 8081:*

What did we just do and why did we do it?
The Default Web Site is the web site that contains our WCF application. Since our WCF application will be using the net.tcp binding type, we need to add the Net.Tcp binding to the Default Web Site so that it will listen for net.tcp requests. By adding the Net.Tcp binding to the web site, we are essentially defining the port number that IIS will listen on for net.tcp protocol requests (i.e. requests that will be made from our WCF client). This is no different than defining the HTTP binding for the Default Web Site in the sense that requests for web pages on the HTTP transfer do so on port 80 (by default). In other words, just as setting the HTTP Binding configures IIS to listen for HTTP requests (browser requests) on port 80, adding the Net.TCP Binding configures IIS to listen for Net.Tcp requests on the specified port.

If we do not configure the net.tcp binding on our IIS Host, exceptions will be thrown.
On the WCF client side, an exception will be similar to:

Could not connect to net.tcp://localhost:8081/WcfServiceHost/Service1.svc. The connection attempt lasted for a time span of 00:00:02.1720000. TCP error code 10061: No connection could be made because the target machine actively refused it 127.0.0.1:8081.

Add the Net.Tcp Protocol

In the previous step we added the Net.Tcp binding to the web site that is hosting our WCF application. Now that the net.tcp binding is added and configured (i.e. IIS is listening for net.tcp protocol requests on port 8081), we need to configure our WCF application to use/enable the net.tcp protocolBy default, IIS only enables the HTTP protocol for its folders, virtual directories, and applications.

In IIS Manager, right-click on the WcfServiceHost application and choose “Manage Application > Advanced Settings…“. Then, add the net.tcp protocol to the Behavior section for Enabled Protocols.

What did we just do and why did we do it?
We have added the WcfServiceHost application to the Default Web Site in an effort to have IIS host our WCF Service. Since our WCF Service will communicate with clients over the net.tcp protocol, we needed to explicitly tell IIS that its WcfServiceHost application will be using the Net.Tcp protocol – i.e. by default, the only protocol that IIS enables for its child applications and folders is the HTTP protocol.

If we do not configure the WcfServiceHost application to enable the Net.Tcp protocol, our WCF Client will receive an exception error similar to:

The message could not be dispatched because the service at the endpoint address ‘net.tcp://localhost:8081/WcfServiceHost/Service1.svc’ is unavailable for the protocol of the address.

Pause for Clarification

Before we move on, I want to clarify the concept of a web application vs. a web site and a binding vs. a protocol.

Web Application vs. Web Site
IIS can support one or more Web Site’s. Each Web Site can contain one or more Virtual Directories (or Folders) and one or more Applications. Out of the box, IIS contains exactly one web site that is named Default Web Site. To the Default Web Site, you can add as many Virtual Directories and Applications that you wish. Technically speaking, the collection of folders, virtual directories, and applications are considered to be the “Web Site”. In short, the Web Site is the host (or the container) of the applications (and folders) that it contains.

A Web Application is a grouping of files (and binaries) that deliver content or provides one or more services over protocols, such as HTTP, HTTPS, and Net.Tcp. When you create or add an application in IIS, the application’s path becomes part of the site’s URL (i.e. http://localhost/WcfServiceHost). For more info, check out Understanding Sites, Applications, and Virtual Directories on IIS 7 and Understanding IIS Bindings, Websites, Virtual Directories, and lastly Application Pools.

A point to note here is that the Default Web Site has configuration properties that affect all of its child folders and applications. Likewise, the folders and applications have configuration properties that affect only that folder or application. So, by enabling the Net.Tcp protocol to our WcfServiceHost application, we have only enabled that protocol for that application.

Binding vs. Protocol
Conceptually speaking, for this example, a binding associates a Protocol (HTTP, HTTPS, Net.Tcp) with a Port Number (:80, :443, :8081) that lives at the Host Address of our Default Web Site (localhost). So, I suppose, a binding inherently turns out to be the same thing as the URL of a site – as in net.tcp://localhost:8081 – That’s our binding for Net.Tcp. The URI to our WCF application is at net.tcp://localhost:8081/WcfServiceHost.

The protocol, on the other hand, is

An agreed-upon format for transmitting data between two devices [ref]

Putting all this together in context of our WCF example, we had to configure IIS to listen for Net.Tcp requests on port 8081 -i.e. any application that uses the Net.Tcp protocol will live at net.tcp://localhost:8081. And then, we configured the WCF application to allow or support the Net.Tcp protocol – meaning that the WCF application will transmit data using the Net.Tcp format and, therefore, only clients that understand the Net.Tcp protocol will have any success communicating with the WCF application.

Create a Self-Signed SSL Certificate and Bind to Port

In order to use the TransportWithMessageCredential security mode when a client passes its Username and Password to the service, WCF requires that we use some form of security. In this example we will use an SSL Certificate – and, to keep things easy (and free), we will use a Self-Signed Certificate. After we create the cert, we will assign the cert to an arbitrary port number.

Keep in mind that using an Self-Signed cert is only good for development purposes. In a production environment you will need to purchase an SSL Certificate from a Certificate Authority (CA). Then you will need to configure IIS by adding an HTTPS binding and associating the certificate with the HTTP protocol. Finally, you will need to update the Web.config file to use the Thumbprint of your “official” SSL Certificate.

Create the SSL Certificate

  1. Open IIS Manager and open Server Certificates
  2. Click the “Create Self-Signed Certificates….” link
  3. Enter a name for the certificate
  4. Open the certificate and copy the Thumbprint

Assign SSL Certificate to a Port

Open a command prompt and enter the following command..

netsh http add sslcert ipport=0.0.0.0:5150 certhash=e2209530871c83319c817b0d7da47620d54d0ba6 appid={B8CA0613-6250-4DDB-A693-74B8678C2DF6}

  • The ipport 5150 is an arbitrary value. You can use any port number that you know is not being used.
  • The certhash is the Thumbprint of the certificate – Note the spaces are removed!
  • The appid is a GUID in Registry Format. (You can use Visual Studio to create the GUID by going to Tools > Create GUID)

WCF Client – Windows Form

The consumer of this example WCF Service will be a Windows Form. In the Windows Form project we will create a proxy for our example WCF service, then we will invoke the service operations by, simply, clicking a Button.

Add a new Windows Form project to the solution

Add a Service Reference to the WindowsFormApplication1 project

Adding the Service Reference using Visual Studio will generate a handful of files known collectively as the “service proxy”. It’s worth the time to take a gander at all the files that were generated – especially the Reference.cs and Service1.wsdl. To see the Service Reference files you will need to turn on “Show All Files”

In addition to the proxy files, an “app.config” file was generated.
The interesting section of the app.config file that you should be familiar with is the <client> section which looks similar to this:

        <client>
            <endpoint address="http://localhost/WcfServiceHost/Service1.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
                contract="ServiceReference1.IService1" name="BasicHttpBinding_IService1" />
            <endpoint address="net.tcp://dv9500t:8081/WcfServiceHost/Service1.svc"
                binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IService1"
                contract="ServiceReference1.IService1" name="NetTcpBinding_IService1">
                <identity>
                    <servicePrincipalName value="host/DV9500T" />
                </identity>
            </endpoint>
        </client>

Notice the two endpoints were created. For this example we are mostly interested in the endpoint that uses the net.tcp protocol. Notice how the address points to the same port (8081) that we defined earlier when added the net.tcp binding in IIS. This is no coincidence! This is the endpoint that we will be using throughout the example.

Add some UI to the client form…

Add event handlers for the Button click events. The handlers will instantiate the proxy and invoke the service operations. Finally, the client will display the response that it received from the service.

        private void btnGetData_Click(object sender, EventArgs e)
        {
            //Since more than one endpoint configuration exists in the app.config,
            //specify the configurationBinding that we want to use.
            using (ServiceReference1.Service1Client proxy =
                new ServiceReference1.Service1Client("NetTcpBinding_IService1"))
            {
                string response = proxy.GetData(Convert.ToInt32(this.nudGetDataInput.Value));
                this.txtGetDataResponse.Text = response;
            }
        }

        private void btnGetDataContract_Click(object sender, EventArgs e)
        {
            using (ServiceReference1.Service1Client proxy =
                new ServiceReference1.Service1Client("NetTcpBinding_IService1"))
            {
                //Create a CompositeType object, then pass it to the service
                ServiceReference1.CompositeType ct = new ServiceReference1.CompositeType();
                ct.BoolValue = this.radGetDataContractTrueInput.Checked ? true : false;
                ct.StringValue = this.txtGetDataContractInput.Text;
                ServiceReference1.CompositeType ctResponse = proxy.GetDataUsingDataContract(ct);

                //Fill the form with the values of the CompositeType
                //object that was returned from the service
                this.txtGetDataContractResponse.Text = ctResponse.StringValue;
                this.radGetDataContractTrueOutput.Checked = ctResponse.BoolValue;
                this.radGetDataContractFalseOutput.Checked =
                    !this.radGetDataContractTrueOutput.Checked;
            }
        }

At this point you can run the client and invoke the service operations.

It’s now time to implement the hooks that will require the client to pass a username and password in order to use the service operations.

Client Authentication

To implement client authentication we will:

  1. Update the hosts Web.config file
  2. Add a custom MembershipProvider class to the host that will authenticate the client credentials
  3. Update the service proxy on the client (update the Service Reference)
  4. Update the client form to allow the user to enter a username and password
  5. Update the client code to pass the username/password to the service

Update Web.config

Create a netTcpBinding
Define a binding that will use the TransportWithMessageCredential security mode and use the UserName clientCredentialType that the endpoint will use.

Add the following configuration to the <system.serviceModel> section of the Web.config file:

    <bindings>
      <netTcpBinding>
          <binding name="Tcp_TransportWithMessageCredentialBinding">
         <em> <security mode="TransportWithMessageCredential">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </netTcpBinding>
    </bindings>

Define a service behavior that allows the metadata exchange to occur over HTTP, specifies the Self-Signed cert that will be used for the Message Credential, and specifies that a MembershipProvider will be used for the username/password validation.

Add the following configuration to the <system.serviceModel> section of the Web.config file:

    <behaviors>
      <serviceBehaviors>
        <behavior name="CertAndAuthenticationBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceCredentials>
           <serviceCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" findValue="e2 20 95 30 87 1c 83 31 9c 81 7b 0d 7d a4 76 20 d5 4d 0b a6"/>
           <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="CustomMembershipProvider"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>

Notice the findValue (of the serviceCertificate element) value matches the Thumbprint of the Self-Signed Certificate that we created earlier (spaced included!).

Define the custom MembershipProvider that we want the CertAndAuthenticationBehavior behavior to use when doing the authentication.

Add the following configuration to the <configuration> section of the Web.config file:

<system.web>
    <membership defaultProvider="CustomMembershipProvider">
      <providers>
        <add name="CustomMembershipProvider" type="ServiceMembershipProvider" writeExceptionsToEventLog="true" applicationName="/"/>
      </providers>
    </membership>
  </system.web>

We will create the custom Membership Provider (ServiceMembershipProvider) immediately after we finish updating the Web.config file.

Specify the service and the behavior it will adhere to.

Add the following configuration to the <system.ServiceModel> section of the Web.config file:

    <services>
      <service name="WcfServiceLibrary.Service1"
               behaviorConfiguration="CertAndAuthenticationBehavior">
      </service>
    </services>

Define the net.tcp endpoint for the service. The endpoint will specify the location of the service, the type of binding to use, and the configuration of the binding.
Add the following configuration to the <service> section that you added in the last step.

<endpoint address="net.tcp://localhost:8081/WcfServiceHost/Service1.svc"
                  binding="netTcpBinding"
                  bindingConfiguration="Tcp_TransportWithMessageCredentialBinding"
                  contract="WcfServiceLibrary.IService1"/>

Here’s the complete Web.config file – Your .config file may differ slightly, but keep in mind that this Web.config is an example of the bare essentials that are necessary to implement Username/Password authentication over a secure transport (net.tcp) and encrypted messaging (Message tranfer):

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="WcfServiceLibrary.Service1"
               behaviorConfiguration="CertAndAuthenticationBehavior">
        <endpoint address="net.tcp://localhost:8081/WcfServiceHost/Service1.svc"
                  binding="netTcpBinding"
                  bindingConfiguration="Tcp_TransportWithMessageCredentialBinding"
                  contract="WcfServiceLibrary.IService1"/>
      </service>
    </services>
    <bindings>
      <netTcpBinding>
        <binding name="Tcp_TransportWithMessageCredentialBinding">
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="CertAndAuthenticationBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceCredentials>
            <serviceCertificate storeLocation="LocalMachine"
                                storeName="My"
                                x509FindType="FindByThumbprint"
                                findValue="e2 20 95 30 87 1c 83 31 9c 81 7b 0d 7d a4 76 20 d5 4d 0b a6"/>
            <userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
                                    membershipProviderName="CustomMembershipProvider"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.web>
    <membership defaultProvider="CustomMembershipProvider">
      <providers>
        <add name="CustomMembershipProvider"
             type="ServiceMembershipProvider"
             writeExceptionsToEventLog="true"
             applicationName="/"/>
      </providers>
    </membership>
  </system.web>
</configuration>

Before we can rebuild/compile the solution, we need to create the Membership provider that is specified in the <providers> section of the Web.config file.

Custom MembershipProvider

In Visual Studio’s Solution Explorer, add a new class to the Web Host’s App_Code folder and name it ServiceMembershipProvider.cs.
Make sure the following assembly references are added to your project:

  • System.Configuration
  • System.Web
  • System.Web.ApplicationServices

Add a using directive to System.Web.Security, then inherit the System.Web.Security.MembershipProvider class.

The custom membership provider class will look something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.ApplicationServices;
public class ServiceMembershipProvider : MembershipProvider
{
	....

Implement the MembershipProvider class

Add the following code to the ValidateUser() method:

    public override bool ValidateUser(string username, string password)
    {
        bool validated = false;
        if ((username.Length > 3 && username.Contains("wcf")) && (password.Length > 6 && password.Contains("pass")))
            validated = true;

        return (validated);
    }

The code above simulates an authenticator for the username and password. You will want to replace the simulation with a call to your assembly that does real validation.

Now rebuild the solution and update the client’s Service Reference.
The client’s app.config will look something like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <netTcpBinding>
                <binding name="NetTcpBinding_IService1" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
                    hostNameComparisonMode="StrongWildcard" listenBacklog="10"
                    maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"
                    maxReceivedMessageSize="65536">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="TransportWithMessageCredential">
                        <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
                        <message clientCredentialType="UserName" />
                    </security>
                </binding>
            </netTcpBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://localhost:8081/WcfServiceHost/Service1.svc"
                binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IService1"
                contract="ServiceReference1.IService1" name="NetTcpBinding_IService1">
                <identity>
                    <dns value="DV9500T" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

Add Username and Password Input To Client Form

Now that all the hooks are in place for the username/password authentication, we need to update the client UI to allow the user to enter a username and password.

Finally, add the code that will pass the client credentials to the service.

proxy.ClientCredentials.UserName.UserName = this.txtUsername.Text;
proxy.ClientCredentials.UserName.Password = this.txtPassword.Text;

Here’s what the code looks like on the client:

        private void btnGetData_Click(object sender, EventArgs e)
        {
            //Since more than one endpoint configuration exists in the app.config,
            //specify the configurationBinding that we want to use.
            using (ServiceReference1.Service1Client proxy =
                new ServiceReference1.Service1Client("NetTcpBinding_IService1"))
            {
                //Send credentials
                proxy.ClientCredentials.UserName.UserName = this.txtUsername.Text;
                proxy.ClientCredentials.UserName.Password = this.txtPassword.Text;

                string response = proxy.GetData(Convert.ToInt32(this.nudGetDataInput.Value));
                this.txtGetDataResponse.Text = response;
            }
        }

        private void btnGetDataContract_Click(object sender, EventArgs e)
        {
            using (ServiceReference1.Service1Client proxy =
                new ServiceReference1.Service1Client("NetTcpBinding_IService1"))
            {
                //Send credentials
                proxy.ClientCredentials.UserName.UserName = this.txtUsername.Text;
                proxy.ClientCredentials.UserName.Password = this.txtPassword.Text;

                //Create a CompositeType object, then pass it to the service
                ServiceReference1.CompositeType ct = new ServiceReference1.CompositeType();
                ct.BoolValue = this.radGetDataContractTrueInput.Checked ? true : false;
                ct.StringValue = this.txtGetDataContractInput.Text;
                ServiceReference1.CompositeType ctResponse = proxy.GetDataUsingDataContract(ct);

                //Fill the form with the values of the CompositeType
                //object that was returned from the service
                this.txtGetDataContractResponse.Text = ctResponse.StringValue;
                this.radGetDataContractTrueOutput.Checked = ctResponse.BoolValue;
                this.radGetDataContractFalseOutput.Checked =
                    !this.radGetDataContractTrueOutput.Checked;
            }
        }

Note that in our example, the Username authentication is based on the following two conditions:

  1. Username must be greater than three characters
  2. Username must contains the word “wcf”

and the Password authentication is based on the following two conditions:

  1. Password must be greater than six characters
  2. Username must contains the word “pass”

When experimenting with this WCF client, you will notice that when you use an invalid Username and/or Password an exception is thrown that reads:

An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.

You will also notice that the exception is thrown only when we call an operation of the service – Not when we instantiate the proxy or set the ClientCredentials.

Summary

In this project we created a WCF Service and had that service hosted in IIS 7. The interesting part of this project was NOT what the WCF service did, but rather, what we had to do to force the WCF client to pass a Username and Password so that the WCF service can authenticate the user prior to allowing the client to run any of the services operations.

The service implemented Transport AND Message Security – and therefore, we demonstrated how to create a Self-Signed SSL Certificate to use in our development and debugging of the service.

Also, we demonstrated how to create our own custom Membership Provider and override the ValidateUser() method so that we can implement our own procedure for authenticating the WCF client – i.e. we can authenticate by checking against a database or even another WCF service!

Download the source files here

Convince WCF To Trust Self-Signed Certificates

When using Self-Signed Certificates in a WCF Service, you may receive the error

SecurityNegotiationException was unhandled: Could not establish trust relationship for the SSL/TLS secure channel with authority ‘localhost:8080’ (your service’s url may be different).

A common cause for the exception is due to the fact that the WCF runtime does not trust Self-Signed Certificates by default. It is, however, possible to override this default behavior.

First we will create a class that does the work of convincing the WCF runtime that our Self-Signed Certificate is trusted.

using System.Security.Cryptography.X509Certificates;
using System.Net;

namespace WindowsClient
{
    class PermissiveCertificatePolicy
    {
        //The name of the certificate
        string subjectName;
        static PermissiveCertificatePolicy currentPolicy;

        PermissiveCertificatePolicy(string subjectName)
        {
            this.subjectName = subjectName;

            //Set the ServerCertificateValidationCallback property to our custom validator method
            ServicePointManager.ServerCertificateValidationCallback +=
              new System.Net.Security.RemoteCertificateValidationCallback(RemoteCertValidate);
        }

        public static void Enact(string subjectName)
        {
            currentPolicy = new PermissiveCertificatePolicy(subjectName);
        }

        bool RemoteCertValidate(object sender, X509Certificate cert, X509Chain chain, System.Net.Security.SslPolicyErrors error)
        {
            //This is our custom validator method. The methods signature is defined simply to satisfy the 
            //RemoteCertificateValidationCallback delegate. 
            //Our custom validator will ignore the parameters and simply return true to convince the WCF runtime
            //that the self-signed certificate can be trusted.
            return true;
        }
    }
}

Now that we have a class that can override WCF’s rejection of a Self-Signed Certificate, we will have the client use that class prior to instantiating the service proxy.

private void InstantiateServiceProxy()
        {
            //Convince WCF that our Self-Signed Certificate is trusted
            PermissiveCertificatePolicy.Enact("CN=MySelfSignedCert");

            //Instantiate the service proxy using the basicHttpBinding
            proxy = new MyWcfService("MyEndpoint_BasicHttp");
        }

How to Configure a TCP Port with an SSL Certificate

Scenario: You have a WCF service that uses the basicHttpBinding binding and you would like to configure the basicHttpBinding with an SSL certificate.

This post will walk you through using IIS Manager and a Command Prompt for creating, configuring, and installing the SSL certificate.

Create a Self-Signed Certificate

Under production scenarios you will not use a Self-Signed Certificate, but to get good idea as to how to configure a TCP port with an SSL cert, a self-signed cert is sufficient.

Open IIS Manager, then open “Server Certificates”

In the Server Certificates window, click the “Create Self-Signed Certificate” link, give the cert a name, then click “OK”.

We will eventually need the Thumbprint of the certificate. So, double-click the certificate that you just created and copy the “Thumbprint” value to the clipboard.

Configure a TCP Port with the SSL Certificate

Copy the Thumbprint of the SSL cert

Open a command prompt and enter:

netsh http add sslcert ipport=0.0.0.0:8080 certhash=bb14d78228ff2c8965de040c2cc1a1fff3132d76 appid={B8CA0613-6250-4DDB-A693-74B8678C2DF6}

The certhash is the certificate’s thumbprint (Note that you must remove the spaces that may exist in the thumbprint!). The appid is an arbitrary GUID (Note: You can easily create a GUID using Visual Studio’s “Create Guid” tool. Just make sure that you choose the “Registry Format” option when creating the GUID.

If you are running a WCF service and fail to assign a certificate with the port that your service is running on, you may receive any one of the following exceptions:

An error occurred while making the HTTP request to https://localhost:8080/CustomersService. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server.

An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.

If you receive any one of the exceptions above, you may resolve the exception by creating a new self signed cert and assigning it to your service’s port.

How To Delete an SSL Certificate From a Port Number

Open a command prompt and use Netsh.

Netsh http delete sslcert ipport=0.0.0.0:8005

Where :8005 is the port number that was associated with the SSL cert.

If everything went well, the netsh command will respond with “SSL Certificate successfully installed.”

Print All SSL Certificate Bindings

If you would like to view the existing SSL Certificate Bindings (or list of ports that are assigned to an SSL Certificate), run the following netsh command:

netsh http show sslcert

The netsh command above will return a list of binding info that looks something like this:

C:\Windows\system32>netsh http show sslcert

SSL Certificate bindings:
-------------------------

IP:port : 0.0.0.0:443
Certificate Hash : 77e20073484988523a67fe6ea3e43e569e4ded37
Application ID : {4dc3e181-e14b-4a21-b022-59fc669b0914}
Certificate Store Name : MY
Verify Client Certificate Revocation : Enabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Enabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
Negotiate Client Certificate : Disabled

IP:port : 0.0.0.0:56789
Certificate Hash : bff283154709a0e0ff5c3fc8d8b4567e1a5d9999
Application ID : {38afa4c0-5eba-427a-aff7-e612ed8fc4f0}
Certificate Store Name : (null)
Verify Client Certificate Revocation : Enabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Enabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
Negotiate Client Certificate : Disabled

Security

In some cases you may get an exception error that states:

Keyset does not exist.

Typically, this exception is thrown when your certificate is being used by IIS (I’ve experienced this error when I was running a WCF service hosted by IIS) and IIS does not have rights to perform a signature. To give IIS rights to the cert;

  1. Open C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys
  2. Open the Security settings for the key in question – You can usually guess which key is the one you want to configure by looking at the “Date Modified” value.
  3. Add the “Network Service” user name to the list of users and grant the Network Service user “Full control”.

For more netsh command, check out Technet.

WCF Encryption

Although WCF supports many different types of bindings, there are only three types of encryption that WCF supports.

WCF Encryption Types

  1. None – Bindings that use the “None” encryption has no encryption whatsoever
  2. Message – Bindings that use the “Message” encryption will encrypt the data that goes back and forth from the client and service
  3. Transport – Bindings that use the “Transport” encryption will NOT encrypt the messages, but will encrypt the TCP packets that go back forth from the client and service.

Here are three examples of the messages that go back and forth between a client and a WCF service that show you the None, Message, and Transport encryption. The bindings that we use for these examples are: 1. basicHttpBinding 2. wsHttpBinding and 3. netTcpBinding

BasicHttpBinding Message

The BasicHttpBinding does not use encryption. Take a look at the message below that was sent from the service to the client. You will notice that the service sent a list of customer information and that the list is readable and not secure.

<MessageLogTraceRecord>
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/ICustomersService/ListCustomersResponse
</s:Header>
<s:Body>
<ListCustomersResponse xmlns="http://tempuri.org/">
<ListCustomersResult xmlns:d4p1="http://schemas.datacontract.org/2004/07/CustomersServiceLibrary" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:Customer>
<d4p1:CompanyName>Alfreds Futterkiste
<d4p1:CustomerId>ALFKI
d4p1:Customer>
<d4p1:Customer>
<d4p1:CompanyName>Ana Trujillo Emparedados y helados
<d4p1:CustomerId>ANATR
d4p1:Customer>
<d4p1:CompanyName>Wolski  Zajazd
<d4p1:CustomerId>WOLZA
d4p1:Customer>
ListCustomersResult>
ListCustomersResponse>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>

WsHttpBinding Message

The WsHttpBinding does use encryption. Take a look at the message below that was sent from the service to the client. The first set of XML is the data that the service created prior to sending it to the client. The second set of XML is the encrypted message that the service sent over the wire to the client.

The message before encryption…

<MessageLogTraceRecord>
xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/ICustomersService/ListCustomersResponse</a:Action>
</s:Header>
<s:Body>
xmlns="http://tempuri.org/">
xmlns:d4p1="http://schemas.datacontract.org/2004/07/CustomersServiceLibrary" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:Customer>
<d4p1:CompanyName>Alfreds Futterkiste
<d4p1:CustomerId>ALFKI
d4p1:Customer>
<d4p1:Customer>
<d4p1:CompanyName>Ana Trujillo Emparedados y helados
<d4p1:CustomerId>ANATR
d4p1:Customer>
<d4p1:Customer>
<d4p1:CompanyName>Antonio Moreno Taquería
<d4p1:CustomerId>ANTON
d4p1:Customer>
<d4p1:Customer>
<d4p1:CompanyName>Around the Horn
<d4p1:CustomerId>AROUT
d4p1:Customer>
<d4p1:Customer>
<d4p1:CompanyName>Berglunds snabbköp
<d4p1:CustomerId>BERGS
d4p1:Customer>
</ListCustomersResult>
</ListCustomersResponse>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>

The message after encryption…
Note the cipher values were truncated for brevity

<MessageLogTraceRecord>
xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1" u:Id="_2">http://tempuri.org/ICustomersService/ListCustomersResponse</a:Action>
RelatesTo u:Id="_3">urn:uuid:40e0f79c-4f0a-4b9d-8ecb-c92d589c867e
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="uuid-ec955797-de1d-4fad-87e5-0ab3e8987068-11">
2010-09-14T16:18:09.733Z
2010-09-14T16:23:09.733Z
</u:Timestamp>
DerivedKeyToken u:Id="uuid-ec955797-de1d-4fad-87e5-0ab3e8987068-7" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
SecurityTokenReference>
uuid:3085906f-ea5b-407d-8b71-fe606433748d" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct">
</o:SecurityTokenReference>
<c:Offset>0</c:Offset>
<c:Length>24</c:Length>
<c:Nonce>
<!-- Removed-->
</c:Nonce>
DerivedKeyToken>
<c:DerivedKeyToken u:Id="uuid-ec955797-de1d-4fad-87e5-0ab3e8987068-8" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
uuid:3085906f-ea5b-407d-8b71-fe606433748d" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct">
</o:SecurityTokenReference>
<c:Nonce>
<!-- Removed-->
</c:Nonce>
</c:DerivedKeyToken>
ReferenceList xmlns:e="http://www.w3.org/2001/04/xmlenc#">
DataReference URI="#_1">
<e:DataReference URI="#_4"></e:DataReference>
</e:ReferenceList>
EncryptedData Id="_4" Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:e="http://www.w3.org/2001/04/xmlenc#">
EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference>
ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/dk" URI="#uuid-ec955797-de1d-4fad-87e5-0ab3e8987068-8">
</o:SecurityTokenReference>
KeyInfo>
<e:CipherData>
V79JZafU+fAhkafqBOkZ0rdMwtqEqh
</e:CipherData>
EncryptedData>
</o:Security>
</s:Header>
<s:Body u:Id="_0">
xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"></e:EncryptionMethod>
xmlns="http://www.w3.org/2000/09/xmldsig#">
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/dk" URI="#uuid-ec955797-de1d-4fad-87e5-0ab3e8987068-8">
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
Mg5F29dMPn5VEHva/H85KAB2K97pLXaqyOXdvFey2NcLTeQgNdMAS+JyOy4O52Oi3ECVVof3iM9q434E4gs=
</e:CipherData>
</e:EncryptedData>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>

NetTcpBinding Message

The NetTcpBinding uses encryption, but not on the message. The encryption occurs on the TCP packets when sent over the wire – known as Transport Security.

Since the messages aren’t encrypted, the message will look the same (in the .svclog file) as the BasicHttpBinding messages – So, there is no need to show you what a NetTcpBinding message looks like. To view the encrypted packet, we would need to use a tool such as WireShark – which I don’t have the time to do right now :)

WCF Callback Hooks

In order to build asynchronous callbacks from a WCF service to client, you will need to add some hooks to your existing WCF Service, Host, and Client. Here is a list of hooks that you can implement to get a rough idea as to how to build service callbacks in your application.
Download the example here.

Example Model

This example is a three-tier model which consists of:

  1. The Service Library (WcfServiceLibrary1.Service1 and WcfServiceLibrary1.IService1). This library represents the business logic and the WCF contracts and operations.
  2. The Service Host. In this example the host was provided by IIS. However, to implement WCF callbacks, the only hook you need to worry about on the host is making sure you use a duplex binding.
  3. The Client (WcfClient.Form1 and WcfClient.Callback)

The WCF Callback Hooks

  1. The service needs to create an additional contract (interface) that describes the callback method.
    namespace WcfServiceLibrary1
    {
        //The contract that defines the callback operation
        interface ICallback
        {
            //Decorate the contract with an OperationContract attribute and specify the operation as One-way method.
            [OperationContract(IsOneWay = true)]
            void DoCallback(string message);
        }
    }
    

    Note: If you do not specify the IsOneWay=true parameter you run the risk of receiving the exception;

    System.TimeoutException: This request operation sent to [the service location] did not receive a reply within the configured timeout (00:00:59.9940000). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.

  2. The client will create a class that implements the service’s callback interface.
    namespace WcfClient
    {
        //This client class must implement the services callback contract
        public class Callback : ServiceReference1.IService1Callback
        {
            //This is the method that the service will call when it makes a call back to the client
            public void DoCallback(string message)
            {
                //In this example, the message is supplied by the service
                System.Windows.Forms.MessageBox.Show(message);
            }
        }
    }
    
  3. The main service contract will:
    1. Associate the callback contract with itself
      namespace WcfServiceLibrary1
      {
      //Associate the callback contract with this contract
      [ServiceContract(CallbackContract=typeof(ICallback))]
      public interface IService1
      {
      ....
      
    2. Create an operation that forces the service to support the callback method (the client will call this method).
      namespace WcfServiceLibrary1
      {
      [ServiceContract(CallbackContract=typeof(ICallback))]
      public interface IService1
      {
      [OperationContract]
      string GetData(int value);
      
      //Define the callback method
      [OperationContract(IsOneWay = true)]
      void Callback();
      }
      }
      
  4. The service will need an operation (method) that the client will call via the service proxy.
    namespace WcfServiceLibrary1
    {
        public class Service1 : IService1
        {
            //Operations that are available to the client via the service proxy
            public string GetData(int value)
            {
                return string.Format("You entered: {0}", value);
            }
    
            //This is the method the client will call via the service proxy
            public void Callback()
            {
                //To callback to the client, the service needs a reference to that client.
                //Create the channel that the service uses to callback to the client with
                //The magic of the GetCallbackChannel<T>() is possible because the client specified the
                //channel when it instantiated the service via the InstanceContext() method.
                ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
    
                //
                //Here is where you put the work that the service is going to do prior to sending the callback to the client
                //
    
                //Pause for a few seconds to simulate work
                System.Threading.Thread.Sleep(5000);
    
                //Get the current time to show work took some time.
                DateTime now = DateTime.Now;
    
                //Notify the client that the service completed its work.
                callback.DoCallback(string.Format("I am the WCF service letting you know I did something.\nTime: {0}", now.ToString("MMM d h:mm:ss")));
            }
        }
    }
    
  5. The service must use a binding that supports duplex communication. Here’s an example of defining the wsDualHttpBinding binding in the .config file of the service host.
          <service name="WcfServiceLibrary1.Service1">
            <endpoint address="" binding="wsDualHttpBinding" contract="WcfServiceLibrary1.IService1">
              <identity>
                <dns value="localhost" />
              </identity>
            </endpoint>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
          </service>
    

    Note the client’s .config file must use the same binding as the service host!

  6. The client will specify context and channel information, then call the service’s callback operation
    namespace WcfClient
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                //Instantiate the class that handles the callback from the service
                Callback callback = new Callback();
    
                //Remember that in order for the service to callback to the client, the service needs a reference to the client!
                //Here is where the client sends context information to the service that allows the service to make the callback.
                //The InstanceContext stores context information and references to the channels that are being used.
                var context = new InstanceContext(callback);
    
                //Instatiate the service proxy and pass the context to the constructor
                ServiceReference1.Service1Client proxy = new ServiceReference1.Service1Client(context);
    
                //Call the operation that has no callback implementation
                string response = proxy.GetData(DateTime.Now.Millisecond);
                this.label1.Text = response; //Display the service response
    
                //Call the operation that has a callback
                proxy.Callback(); //The callback will be handled by the client's Callback class
            }
        }
    }
    

    Note that the call to proxy.Callback() is asynchronous which means that the client can do something else while the service is doing the callback work. In other words, the client doesn’t have to wait around for the service to make the callback!

Tracing & Logging WCF Applications

WCF offers Tracing capabilities, but are turned off by default. To make use of WCF Tracing you will need to turn on the feature and make a few configurations to the .config files of the modules that you would like to trace.

Enabling Tracing via Service Configuration Editor

  1. In Visual Studio, open the solution that contains the projects that you would like to trace
  2. Right-click a .config file of a project that you want to trace, then select “Edit WCF Configuration”

    Open .config file with the SCE

  3. In the Service Configuration Editor, open the Diagnostics directory then click the “Enable MessageLogging” and “Enable Tracing” links

    Enable logging and tracing

  4. Click the “ServiceModelMessageLoggingListener and ServiceModelTraceListener links, then choose where you want the logs to be saved.

    Specify where you want your logs to be saved

  5. Click the “Message Logging” item in the left menu, then specify which messages you want to log

    Specify the messages you want to log

  6. Open the “Sources” directory, click the “System.ServiceModel” and “System.ServiceModel.MessageLogging” sub items, then set the “Trace level:” to Verbose (or whatever level you want)

    Set the Trace Levels to Verbose

  7. Save the configuration, then take a look at the .config file

    Tracing and Logging Configuration shown in the .config

You will want to enable Tracing and Logging on each .config file that you want to view logs for.

To view the logs, go to the path that you defined in Step 4.