I have meant to write a blog post about client certificates in a development environment for a while now, as every time I come to implement it I forget a step somewhere. There are resources online for a number of the steps below, but I wanted to consolidate that information along with my own steps into a single place that I can use as a reference. This may also be useful to others, hence why I am making a blog post rather than file it away in my mesh repository somewhere.
So, what am I trying to do here? Well, I would like to create a WCF service that utilises SSL and requires that clients use a certificate for authentication in a development environment where I do not have any certificates supplied to me. I am setting this up in a Windows 2008 environment (so IIS 7) with .NET 3.5. The whole process will involve the following steps:
- Create certificate to act as a root certificate authority
- Install root certificate
- Install service certificate on server
- Configure site to use SSL certificate
- Export client certificate
- Import certificate on client
- Configure WCF Service
- Configure client(s)
Step 1 – Create Certificate to act as Root Certificate Authority
To use chain trust validation during development you need to create a self-signed root CA and place it in the Trusted Root Certification Authority store. The certificate used by WCF is then created and signed by the root self-signed certificate and installed in the LocalMachine store.
- Open the Visual Studio command prompt and browse to the location where you want to save the certificate files.
- Enter the following command:
Makecert -n “CN=MyRootCA” -r -sv MyRootCA.pvk mYRootCA.cer
- In the Create Private Key Password dialog box, enter a password, confirm the password, and then click OK
- In the Enter Private Key Password dialog box, enter the password again and then click OK.
- You should now have two files MyRootCA.cer and MyRootCA.pvk
Step 2 – Install Root Certificate Authority
The certificate that will be created using this certificate authority will need to have the CA installed on both the client and the server, as it will be used to verify the certificate.
- Follow these steps on both the server and client(s)
- Run MMC
- Add certificates snap-in
- Choose computer account, as this certificate needs to be made available for all users
- Choose local computer
- Browse certificates snap-in and go to certificates node under trusted root certification authorities
- Right-click this and select import from the popup menu
- Browse to the folder where you created the root CA certificate
- Accept defaults for rest of import wizard
- You can double-click the certificate entry to view and validate it installed correctly
Step 3 – Install Service Certificate on Server
You now want to create the certificate for the service on the server and the following steps will do this. Once completed you will see the certificate via the certificates snap-in of MMC and browsing to the following location: Certificates (Local Computer) -> Personal -> Certificates
- Open the Visual Studio command prompt and browse to the location where you saved the Root CA certificate files
- Run the following command
makecert -sk <KeyName> -iv MyRootCA.pvk -n “CN=<MachineName>” -ic MyRootCA.cer -sr localmachine -ss my -sky exchange -pe
- Where <KeyName> is the subject’s key container name (which should be unique) and <MachineName> is name of the server which must match the DNS or NetBIOS name
- Enter the password you defined previously
Step 4 – Configure Site to use SSL Certificate
We now want to configure the website that is to host the WCF service to use the SSL certificate we just created. This step assumes you have already created a website for HTTPS.
- Open IIS Manager
- Browse to “Sites”
- Right-click on your site and select “bindings” from popup menu
- Click edit and from the SSL certificate drop-down choose the certificate you have just installed
Step 5 – Export Client Certificate
Now we want to export the certificate that will be used on the client(s). At the end of this step you should have a new file created with a pfx extension.
- Open MMC
- Add the certificates snap-in and select “Computer Account”
- Browse to Certificates (Local Computer) -> Personal -> Certificates
- Right-click on the certificate you created (should be name of server), select “all tasks” and then “export”
- In the wizard select that you want to export the private key
- From the next screen select the defaults
- Enter a password to protect the private key
- Select where you want to export the certificate and complete the wizard
Step 6 – Import Certificate on Client
Now we need to import the certificate we just exported onto the client(s). You should have already imported the certificate authority in step 2, but if not make sure you do so at this point. Note that we are using the current user here, it just happens to be the way we have things configured later on. You may have a different setup here.
- In MMC, add certificate snap-in for “current user”
- Browse to Certificates (Current User) -> Personal -> Certificates
- Right click this node and select All Tasks and Import from the popup menu
- Follow the steps in the wizard, selecting the pfx file you exported in the previous step
Step 7 – Configure WCF Service
This is the WCF service that we are hosting and requiring authentication for. We happen to be using basicHttpBinding here, but it could be something else
- Make the following entries in your web.config
<service behaviorConfiguration=”MyBehaviour” name=”MyService”>
<endpoint name=”MyEndPoint” address=”” binding=”basicHttpBinding” bindingConfiguration=”ClientCertificateTransportSecurity” contract=”MyContract” />
<!–<endpoint address=”mex” binding=”mexHttpsBinding” contract=”IMetadataExchange” />–>
<serviceMetadata httpsGetEnabled=”true” />
<serviceDebug includeExceptionDetailInFaults=”true” />
<transport clientCredentialType=”Certificate” />
- Note that if you do not have the mex commented out like above you will receive the error “The SSL settings for the service ‘None’ does not match those of the IIS ‘Ssl, SslNegotiateCert, SslRequireCert, SslMapCert’”.
Step 8 – Configure Client(s)
If using custom app
Make sure you have the following in your configuration file.
<binding name=” ClientCertificateTransportSecurity”>
<transport clientCredentialType=”Certificate” />
Where myThumbprint is available from the certificate
If using a browser to view the service
Should just work, a popup should appear that asks you to specify the certificate to pass
- You will get an error that says “HTTP Error 403.7 – Forbidden” “The page you are attempting to access requires your browser to have a Secure Sockets Layer (SSL) client certificate that the Web server recognizes.”
- In FireFox go to Tools -> Options -> Advanced -> Encryption -> View Certificates
- Click import and select the certificate with private key file you exported earlier (the .pfx file)
- You will need to clear history now or restart browser
You may want to use fiddler to examine what is going on. When you have fiddler open you will receive a message saying “The server [your server] requires a client certificate. Please save a client certificate in the following location”. Pretty self explanatory. You will need to export your certificate from MMC certificates snap-in, certificates -> personal -> certificates but this time do not export the private key, just export as a DER encoded certificate, copy and rename to the folder/name that fiddler wanted you to.
“There was an error downloading ‘https://MachineName/servicefolder/Service.svc’. The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel”
- If CA not installed on client then a trust relationship cannot be established and the above error can occur
“The SSL settings for the service ‘None’ does not match those of the IIS ‘Ssl, SslNegotiateCert, SslRequireCert, SslMapCert’”
- You will get this error if you do not make sure the clientCredentialType is not set to certificate in the service configuration
- If the mex is not commented out in the configuration of the service
- If the binding of the website isn’t set properly (as shown in step 4)