WCF - Let's Communicate (across borders)
This is an addition to my blog post from last friday. If you tried my little sample and it worked perfectly you probably had client and server running on the same physical machine. If you tried to access the server from a different machine the client requesting the service probably failed with a security exception saying that the Security Support Provider Interface failed to negotiate the authentication credentials. Why this worked on the local machine is because of some optimization procedures using NTLM for authentication. A description about what is happening can be found here.
In essence why this is happening at all ist that WCF is secure by default. That means that at least message security is enabled for all bindings except for the BasicBasicHttpBinding which supports BasicProfile 1.0 for interoperability and doesn't use security. For an overview about WCF Security you can consult the securyty section on MSDN.
Especially for internet scenarios it is quite unlikely that client and server reside on the same physical hardware ;-). Therefore to enable my little sample client to communicate with the server and vice versa from remote locations we need to change the configuration of the service and the client. In my case I want to enable anonymous client requests to the service and the service providing service credentials for channel setup based on a x509 certificate. Therefore I had to create and install a self-signed certificate in the certificate store of the service machine. To create the certificat I use the makecert.exe tool. My command line for that looked like this:
makecert -sr LocalMachine -ss My -sky exchange -pe -a sha1 -n CN=WCFTestCert WCFTestCert.cer
This will install a certificate in the certificate store in the LocalMachine tree. After that I need to grant the Network Service account permission to read the file where the private key for the certificate is stored. This cann be done using the findprivatekey.exe tool coming with the Windows SDK. To retrieve the right file I issued the following command and stored the result in a environment variable:
"%MSSDK%\bin\FindPrivateKey.exe" My LocalMachine -n CN^=WCFTestCert -a
Then you can set the read permissions on that file for the using the icacls.exe tool.
icacls.exe "%PK_FILENAME%" /grant:r "NT AUTHORITY\NETWORK SERVICE":R
Then I started to alter the service configuration file. The relevant sections are:
1. There needs to be a binding configuration section for the wsDualhttpBinding with the following minimal attributes:
<security mode = "Message">
2. There must be an addition to the service behaviours configuration to specify the service certificate
<serviceCertificate findValue="WCFTestCert" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
If the service proxy is then regenerated the identity section of the configuration file includes encoded information about the service certificate. In additon the client side configuration has to be altered by adding a behaviour configuration like shown below:
Of course like in all cases above the relevant sections have to be announcend in their respective parent section. Here this would be the behaviourConfiguration attribute in the client section.
One important thing to note is that you need to import the certificate manually for this example in the TrustedPeople section of the CurrentUser store on the client machine if you use the PeerOrChainTrust certificationValidationMode. You can omit this by setting certificateValidationMode="None". However this setting is not recommended for all scenarios but testing.
The last part to enable the sample to work is to open port 80 in the Firewall on both machines in order to allow duplex channel communication.