Monday, May 31, 2010

Configuring Self-Hosted WCF Service with Transport Security (HTTPS)

I just finished what was a four-day nightmare trying to configure a self-hosted WCF service using wsHttpBinding with transport security (meaning it uses an https:// URL), with locally generated test certificates. This all happened on Windows 7 Professional, with Visual Studio 2010 Professional.

The following tools were used in this process:

  • makecert
  • netsh
  • mmc

Here are the steps:

  1. Create a self signed root certificate.
  2. Install the root certificate in the Trusted Root Certification Authorities store of the client and server machines (the same machine in my case).
  3. Create a signed service certificate.
  4. Install the service certificate in the Trusted People store of the client machine.
  5. Associate the service port with the service certificate.
  6. Configure the service.
  7. Configure the client.

Steps 1 through 4 were taken from the WCF Security Guidance codeplex site, from the How To – Create and Install Temporary Certificates in WCF for Transport Security During Development page.

Create a Self Signed Root Certificate

Run the following command:

makecert –n “CN=<<AnyName>>” –r –sv <<AnyName>>.pvk <<AnyName>>.cer

The <<AnyName>> may be different for the 3 occurrences but just for convenience, use the same one. I used RootCATest for mine, and I’ll use that for reference throughout the rest of the post.

image

Install the Root Certificate

Start the Microsoft Management Console by running the command mmc.

image

From the “File” menu select “Add/Remove Snap-in…”

image

On the left, select “Certificates”, and click the “Add >” button.

image

Then check the “Computer account” option (if you are not an administrator of your machine, you may have to settle for “My user account” – but I haven’t tested that).

image

Finish the wizard with the default selections and press “Ok” on the “Add or Remove Snap-ins” dialog.

Expand the “Certificates (Local Computer)” node, right click the “Trusted Root Certification Authorities” node and select “All Tasks –> Import…”. Use the wizard to browse for the certificate file you created in the previous step (RootCATest.cer in this example) and continue with all the default options.

Eventually you should see something like this on the left side:

image

And this on the right side:

image

Create a Signed Service Certificate

Run the following command:

makecert –sk <<KeyContainerName>> –iv RootCATest.pvk –n “CN=localhost” –ic RootCATest.cer –sr localmachine –ss my –sky exchange –pe localhost.cer

Use any name for <<KeyContainerName>> it has no significance. I chose “MyKeyName”:

image

This is taken as is from the howto page, but one thing needs to be stressed (this stumped me for hours because I missed it when reading the howto). The certificate’s subject name (specified with the –n switch) should be the same as the dns entry of the machine running the service. In the case of a single machine used for both the service and the client, using self-hosting, this must be “localhost” (Or rather “CN=localhost” for the command).

Install the Service Certificate

Installing the service certificate is done similarly to installing the root certificate, except it should be stored in the “Trusted People” store.

Associate Service Port with Certificate

Run the following command:

netsh http add sslcert ipport=0.0.0.0:<<port>> certhash=<<certificate thumbprint>> appid={00112233-4455-6677-8899-AABBCCDDEEFF}

Substitute <<port>> for the TCP/IP port number on which your service is configured to listen. To obtain the certificate thumbprint use the MMC tool again, double click on the service certificate in the “Trusted People” store to open its property pages, go to the “Details” tab, click the “Thumbprint” entry and copy the hex codes that appear at the bottom:

image

Use a text editor to remove all the spaces, and substitute that for the <<certificate thumbprint>> above. Here’s what it looks like in my example:

image

The “appid” argument is irrelevant for now, and may be any valid GUID value.

Configure the Service

Here is the “app.config” file I used in my example. It is nothing special, but note several things:

  • httpGetEnabled=”false” – Necessary for HTTPS metadata exchange (mex)
  • security mode=”Transport” and clientCredentialType=”None” – Since my example needs to run in partial trust, I didn’t use any message security or any client credentials. This may be changed if running in full trust.

<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyBehavior">
<serviceMetadata httpGetEnabled="false"/>
<serviceCredentials>
<serviceCertificate
findValue="localhost"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="MyBinding">
<security mode="Transport">
<transport
clientCredentialType="None"
proxyCredentialType="None"
realm=""/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service
name="eftest.MyService"
behaviorConfiguration="MyBehavior">
<endpoint
address=""
binding="wsHttpBinding"
contract="eftest.IItemServices"
bindingConfiguration="MyBinding">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<host>
<baseAddresses>
<add baseAddress=
"https://localhost:8733/MyService/"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>

Configure the Client

Again, here is the “app.config” I used in my client configuration.

<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="MyBinding">
<security mode="Transport">
<transport
clientCredentialType="None"
proxyCredentialType="None"
realm="" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="ServiceCert">
<clientCredentials>
<serviceCertificate>
<authentication
certificateValidationMode="PeerTrust"
revocationMode="NoCheck"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint
address="https://localhost:8733/MyService/"
binding="wsHttpBinding"
bindingConfiguration="MyBinding"
behaviorConfiguration="ServiceCert"
contract="MyService.IItemServices"
name="MyEndpoint">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
</client>
</system.serviceModel>

1 comment:

  1. Thank you very much for this nice how to! Unfortunatelly the images are not shown properly, can you fix that?

    ReplyDelete