Tuesday 13 May 2008

How-to: Create a Server Certificate for a WCF Service

One of the things I took a while to get used to when I started working with WCF were the certificates that are needed to secure Services.

Microsoft have configured WCF to be secure by default. This means that developers need to be much more imaginative in finding ways to open up gaping security holes in applications: there are several incantations you need to perform if you want to send passwords unencrypted over the wire, for example.

As a consequence however, if you do need to exchange security credentials, and are not in a situation where you can use Windows Credentials, you might find yourself needing to specify a Certificate that can be used to encrypt messages exchanged by client and server. You know you need one if you get errors like

System.InvalidOperationException: The service certificate is not provided. Specify a service certificate in ServiceCredentials.

The books I consulted (Learning WCF and Programming WCF Services) talked a lot about creating Certificates that could be used in a test environment (using makecert and the like), but almost nothing about creating certificates for a production environment. Using Test Certificates even during development is also less than ideal: because they are not properly signed by the relevant authorities you have to configure WCF not to do the full security checks on them; apparently there are also some performance problems.

After digging around a bit, I figured out that the X.509 certificates that WCF says it needs are the same thing as the SSL certificates you can purchase from Thawte, Verisign, and co - call me stupid for taking so long, but I haven't seen this actually spelled out. This led me to discover an easier way of generating test certificates. Comodo offer a very nice service letting you create free test SSL certificates that are valid for 90 days. As far as I can tell, they don't stop you recreating the certificates at the end of that time either. Because they don't validate domain names for these test certificates, you can use this service to create certificates for machines on your intranet. Their prices for full certificates seem very reasonable, particularly if you need them only for intranet use.

Getting and setting up a certificate

In the hope that I'll save somebody else some time, these are the steps I follow to set up my WCF service with a certificate:

  1. Generate a Certificate Signing Request (CSR). The easiest way to do this is with IIS. Here's how if you use IIS 5.1 or 6.0 (see the bottom of this post for instructions using IIS7). Don't do this on a server that's serving up a site - it will change the IIS Certificate for the site. You can however create the certificate on one computer and export it to another.
    1. In IIS, right click on one of your websites (there'll only be the Default Web Site if you're doing this in Windows XP), then select Properties. Switch to the Directory Security tab.
    2. Click the Server Certificate button.
    3. In the IIS Certificate Wizard, select "Create a new Certificate".
    4. (If you see three options (Renew, Remove, Replace) that means you already have an SSL certificate configured. If you're sure it's safe to do so, you can select Remove to get back to the position of generating a new one. )
    5. Fill out the screens that follow. The most important one to get right is "Common name". Here you should put the same DNS name that you intend client computers to use when connecting to the Server. If, for example, you only intend to connect from the same machine, then it's fine to use "localhost". Use the server's name if you intend clients to connect over an intranet. For servers being accessed from elsewhere, include the full domain name, e.g. mymachine.mydomain.com.
    6. In the last but one step of the wizard, make sure you note down where IIS puts the Certificate Request File.
  2. Get the certificate signed. This is where you'll need to use a Certification Authority (such as Thawte or Verisign, or one you set up yourself). As I mentioned, I've found that Comodo is the easiest to use for creating Test Certificates.
    1. On the Comodo website look for the "Get It Free Now" link; or one of the other options if you're ready to splash out and buy a full certificate. Follow the link through to the Certificate request page.
    2. At this point you'll need to open up the Certificate Request File that IIS created, and copy-and-paste its contents into the appropriate box on the web page.
    3. Complete the rest of the request at the site, then wait for an email to come through with your certificate zipped up inside it.
  3. Import the certificate into IIS. Now you'll need to hop back to IIS.
    1. Get to the IIS Certificate Wizard as we did in step one.
    2. This time, it should give you the option to "Process the pending request"
    3. Follow through, and select the file that you got back from Comodo, or other CA.
    4. Complete the Wizard: the certificate is now available for use in IIS if you need it.
  4. Make sure the Certificate is available for WCF. To do this we'll need to use the Certificate Manager Snap-in.
    1. Click the Start button, type mmc, then press enter. This will display an empty Management Console.
    2. Click File->Add/Remove Snap-in.
    3. In the Add/Remove dialog, click Add.
    4. Select "Certificates" from the list and click Add. When prompted in the following dialog, select to manage certificates for the Computer account on the Local Computer.
    5. Close and OK back to the main Console Window. The Certificates node should now be there.
    6. Check that a Certificate with your machine's domain name appears in the Personal\Certificates folder.
      CropperCapture[5] If you have created a certificate for localhost, you can use this Console to export the Certificate, and import it on another machine, to save having to create multiple test certificates. The same applies if you have created the certificate on a machine different to the Server that will use it. Right-click the certificate, then select All Tasks->Export to export from this machine; to Import on another machine, bring up this same console, then right click the Personal/Certificates folder and select All Tasks->Import.
  5. Now you're ready to configure WCF. These steps assume you're using the config file to configure your service.
    1. Open the Services config file. Look for the behaviour element that's used by your service. Under it, add a <serviceCredentials> node, then, inside that a <serviceCertificate> node. It should look something like this:
              <behavior name="[YourServiceBehaviourName]">
                  <serviceCertificate findValue="localhost" storeLocation="LocalMachine"
                    storeName="My" x509FindType="FindBySubjectName" />
      Update the findValue attribute to include the domain name of your machine - whatever you put for the Common Name in the Certificate Signing Request. For completeness, I should mention that the value in the storeName attribute corresponds with the top level folder under the Certificates node in the Certificates Snap-in (thus the "Personal" folder becomes the "My" store). If for some reason IIS puts your certificate in a different folder, then you might need to change the value in storeName.
  6. That's it. Your newly secured service is good to go.

Generating Certificate Requests in IIS7

[Update 10/11/08]

Kevan Brewer kindly sent me instructions for generating certificate requests in IIS7. These replace steps 1 and 3 above.

  1. Generate a Certificate Request.
    1. Click Start -> Run and type inetmgr.
    2. Click on the root node in IIS manager and double-click Server Certificates from the centre panel.
    3. In the Actions panel on the right, click on Create Certificate Request...
    4. Fill in the form paying particular attention to the Common Name field on the first page. On the last page, click the browse button, browse to the directory of your choice and enter a file name e.g. testcertificate. (I found that if I just typed a name into the box, it either created the file somewhere random or didn’t create it at all.)
  2. Get the Certificate Signed. This is the same as step 2 above.
  3. Import the Certificate into IIS.
    1. Back in IIS Manager, click on Complete Certificate Request... on the Actions panel.
    2. Browse to the directory to where the zipped certificate files were extracted, change the file type to *.* since, by default, it only shows files with a .cer extension. Select the file which most closely matches the name you entered for the Common Name field in step 1.4.
    3. Enter a suitable name for the friendly name (e.g. test certificate) and click OK.


Anonymous said...

I followed your instructions and for the most part they seem to work alright, but I get a strange error:

Identity check failed for outgoing message. The expected DNS identity of the remote endpoint was 'localhost' but the remote endpoint provided DNS claim 'www.localhost'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity 'www.localhost' as the Identity property of EndpointAddress when creating channel proxy.

It's as if COMODO setup the cert to authenticate against www.localhost instead of plain localhost. I can't seem to find any reference to that www.localhost in the cert, however. Any ideas?



Unknown said...

We had this problem as well. When asking for a certificate for "localhost", "www.localhost" was being included in the Subject Alternative Name part of certificates generated by the instantssl.com site. For some reason WCF doesn't handle this very well.

A colleague has contacted instantssl, and they've now corrected this, so that only the domain name you specify is included.

Very odd about WCF's behaviour though...

Anonymous said...

Why dont you use Makecert.exe to create the test sertificates yourself?

Unknown said...

Because MakeCert isn't terribly easy to use, and also requires WCF to be configured to accept test certificates.

That said, I've just spotted a new GUI tool which (I think) replaces MakeCert. Might be worth a look: http://www.pluralsight.com/community/blogs/keith/archive/2009/01/22/create-self-signed-x-509-certificates-in-a-flash-with-self-cert.aspx

Katamai said...

Many thanks for this article. During reading other ones number of questions raises exponentially, with no answers. You made everything clear. All the best!

Naziya Khan said...

This article is the one that I was looking for and it answers all the questions so clearly and in a simple language. Thanks a lot for saving a lot of my time that would have otherwise wasted

Unknown said...

Glad I could help.

paras said...

wonderfull post

Toyin said...

Could you please explain how you configured the client. Do we need to do any thing fancy on the client side?

ckalyan said...

I have a small issue. I am trying to call a Web Service through my WCF service Web application. We are planning to go for production now.
I am trying to implement Certificate authentication when i call the web service.I have They have provided the Certificate file which i have to use now on my client side.
Please tell me whats wrong in these config tags in my web.config file

I have imported the Certificate file to Personal folder using MMC command.Please let me if i am missing somethings here.

Unknown said...

I'm afraid I don't have any experience of using Certificate authentication to call a web service - this blog post is just about setting up a certificate on the server to enable HTTPS connections.

I would suggest you ask your question on the WCF forums or stackoverflow.com

Anonymous said...

Do the site need to have its own/dedicated IP like in SSL?

SSL Cert said...

Wow, this is an excellent post. I admire your dedication to helping other people with tasks like these and from reading the comments below it doesn't seem to have gone unnoticed. I had a go at following the steps and managed to get through most of it but my computer froze at a vital stage. I plan on giving it another go tonight after running various virus scans, thanks for the walkthrough.

Senthil said...

Sam, Comodo doesnt seem to provide "localhost" anymore

Post a Comment