Click here to Skip to main content
Click here to Skip to main content

SSL with Self-hosted WCF Service

By , 1 Mar 2008
 

Introduction

In order to perform any kind of SSL encryption between a client and a server, there need to be certificates in place. Certificates can seem a bit arcane to the uninitiated, especially when mixed in with some bizarre WCF configuration settings, but never fear, it's all here.

The reason I'm writing this article (my first) is that I really battled to get this working, and the information I need was scattered all over the place. I also ran into a lot of runtime errors which ended up being caused by setup issues much earlier in the process. Very very annoying. Hopefully this will save someone some trouble!

I'm assuming that you are familiar with the basics of WCF and are comfortable with configuration files and the like. I'm not including any source code as the service itself is actually not particularly important, SSL is all about setup.

Certificates

This example assumes that you (like me) don't have access to a nice shiny Certificate Authority (CA) and need to make use of the makecert tool. (Sparse information can be found here.)

Note: I have recently discovered this nifty Website, a free CA, who would have thought about it.

You need one certificate (cert) to act as your root authority, and one to act as the actual certificate to be used for the SSL, which needs to be signed by your root authority. If you don't set up the root authority your single certificate won't be trusted, and you will start to discover this through a series of extremely annoying WCF exceptions, long after the fact.
The following command (run in a the Visual Studio command prompt) will create your root certificate, no, I didn't come up with this myself, you can find some nice examples all over the place, for example here).

makecert -sv SignRoot.pvk -cy authority -r signroot.cer -a
    sha1 -n "CN=Dev Certification Authority" -ss my -sr localmachine

Take a look at the above links to see what each of these arguments mean, it isn't terribly important, but it's nice to know.

Once this command has been run and succeeded, you need to make this certificate a trusted authority. You do this by using the MMC snap in console. Go to the run window and type "mmc", hit enter. Then in the window that opens (called the "Microsoft Management Console, for those who care) perform the following actions.

File -> Add/Remove Snap-in -> Add… -> Double click Certificates -> Select Computer Account and Click Next -> Finish -> Close -> OK

Then select the Certificates (Local Computer) -> Personal -> Certificates node.

Personal Certificates

You should see a certificate called "Dev Certificate Authority" (or whatever else you decided to call it). Move this certificate from the current node to Certificates (Local Computer) -> Trusted Root Certification Authorities -> Certificates node, drag and drop works happily.

Trusted Certificates

Now you have NOT the cert you need :)
You have made yourself able to create trusted certs though, which is nice.
Now you have to create another cert, which you are actually going to use.
Run makecert again, but run it as follows...

makecert -iv SignRoot.pvk -ic signroot.cer -cy end -pe -n
    CN="localhost" -eku 1.3.6.1.5.5.7.3.1 -ss my -sr
    localmachine -sky exchange -sp
"Microsoft RSA SChannel Cryptographic Provider" -sy 12

Note that you are using the first certificate as the author for this latest one. This is important... where I have localhost you need to put the DNS name of your box. In other words, if you deploy your service such that its endpoint reads http://bob:10010/Service then the name needs to be bob. In addition, you are going to need to do this for each host you need to run as (yes, so one for bob and another one for localhost).

Get the signature of your cert by double clicking on the cert (Select the Certificates (Local Computer) ' Personal ' Certificates), opening the details tab, and scrolling down to the "Thumbprint" option.

Thumbprint with carefully sprayed values

Select the thumbprint and copy it. Put it in Notepad or any other text editor and replace the spaces with nothing. Keep this. (Take a look here for more on how to do this, although this link is rather unnecesary.)
You have your certs set up.

Configure a Port with an SSL certificate

Now you get to use another fun tool, httpcfg. (For more information, look here.) Firstly run the command below to check that you don't have anything running on a port you want.

httpcfg query ssl
Mine looks like this

Note that there are two ports, one will work for requests to chrise, one will work for localhost, keep track of which is which.

If this is your first time doing this, it should just return a newline. If there is already SSL set up on the exact IP you want to use (or if later on you need to delete any mistakes) you can use the following command, where the IP and the port are displayed as a result from the previous query.

httpcfg delete ssl -i 0.0.0.0:8888

When you have finished messing about with the above two commands, run the following command to associate a given certificate (thus allowing asymmetric encryption happiness) with a given port.
Note that the IP 0.0.0.0 is a special one indicating every IP on this PC.

httpcfg set ssl -i 0.0.0.0:8012 -h abababababababababababababab

Where the last bit (ab^n) is the thumbprint you kept earlier (you did keep it right?). Great, you now have an SSL enabled port.

The WCF Service

Finally!
Most of the work that needs to be done in WCF service for SSL is in the configuration. I prefer configuration files myself, but this can also be done in code.

The service configuration needs to look like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="NewBehavior">
          <serviceMetadata httpsGetEnabled="true"/>
         </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <basicHttpBinding>
        <binding name="Binding">
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
         </binding>
      </basicHttpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="NewBehavior" 
            name="TestWCF.ServiceImplementation.TestWCFService">
        <endpoint
          address="https://chrise:10081/TestWCFService"
          binding="basicHttpBinding"
          bindingConfiguration="Binding"
          name="TestWCFService.Http"
          contract="TestWCF.ServiceContracts.ITestWCFService" />
        <host>
          <baseAddresses>
            <add baseAddress="https://chrise:10081/TestWCFService" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
 </configuration>

Note that the httpsGetEnabled is set to true. This means that even your mex is encrypted, nifty! Also note that chrise which is the hostname in this case MUST have a corresponding certificate, or no dice.
I'm using the basic HTTP binding. You could do it with wsHTTP as well, but what's the point of getting encrypted messages if your entire transport is encrypted anyways?
Note that clientCredentialType is set to none, this is just to simplify the whole solution, otherwise we get into a whole other article.

The client looks like this:

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="TestWCFService.Http">
                <security mode="Transport">
                    <transport clientCredentialType="None" 
                        proxyCredentialType="None"
                        realm="" />
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>
    <client>
        <endpoint address=https://chrise:10081/TestWCFService 
                binding="basicHttpBinding"
            bindingConfiguration="TestWCFService.Http" 
                contract="TestWCF.Client.TestWCFService.ITestWCFService"
            name="TestWCFService.WSHttp" />
    </client>
</system.serviceModel>

And that should work!

Some Exceptions I Ran Into

Firstly, this will only work on your box. Why? Because no other computer in this world is dumb enough to trust your personal cert. In order to actually get your client working on another PC, you need to export your authority cert and your SSL cert, and then import them on the client's PC. This is why makecert is NOT FOR PRODUCTION. Get a real CA guys.

Secondly, you should be able to browse to the endpoint when the service is running. If when you do so, you get a popup asking you about the trustworthiness of the cert, your cert isn't trusted and the client won't be able to talk to the server.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

anarchistic
Software Developer (Senior)
United Kingdom United Kingdom
Member
Chris is a full time software developer in London, UK.
He has a BSc in computer science and is busy taking courses in the MCTS stream.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberJerleth.1 Feb '13 - 4:38 
It works!
GeneralMy vote of 5membervlad_pol@hotmail.com17 Aug '12 - 5:16 
it works!
GeneralMy vote of 5memberromull28 May '12 - 3:35 
Thanks!
QuestionThank youmemberCraig James16 May '12 - 22:13 
Nice article, works well. Just want to point out if you have win 7 / Vista to run the command prompt in Administrator mode. Took me a minute of head scratching to figure that out.
QuestionThank you ChrismemberMember 777629326 Apr '12 - 3:53 
Thanks so much for this. I was working on a project here[^]that had me going nuts for a few days because i didn't have the XP codes to make it work.
GeneralMy vote of 5memberMember 777629326 Apr '12 - 3:48 
Been looking for this for a few days. Really great work.
QuestionAccessing this services from different machinememberdheenadhayalan24 Jan '12 - 0:27 
I tried to access the service from a different machine by giving the IP of the server ie where the wcf service is running. But the service is not accessible. Any help?
QuestionNot working in Win2003memberMember 145720923 Jun '11 - 17:42 
Hi,
 
This works perfect in WinXp. When I tried to use the same in Win2003 it does not work. Any specific settings to be done for Win2003?
QuestionStill unable to consume HTTPS hosted WCF service in OOB Silverlight 4 applicationmembercjmakwana19 Jun '11 - 4:52 
Hello,
 
I exactly followed the steps mentioned in the article, for the case httpcfg. This is because I am using IIS 7.5 and it supports netsh instead of httpcfg. Hence, I used netsh equivalent commands. With this, I was able to publish the WCF service with the HTTPS bindings enabled. I was also able to browse the WCF service, and its WSDL. The things work well when the Silverlight application is run inside of browser.
 
But when invoked out of browser, it doesn't run, causing the same exception - NotFound.
 
Also note that my Personal certificate store alreay has the second certificate created, as mentioned in the article.
 
All these functionalities are confined to my machine only, i.e. there are no cross domain or cross machine calls.
 
Please let me know your suggestions on this ASAP.
 
Thanks in advance.
 
Chandresh.
GeneralMy vote of 5member4izh25 May '11 - 8:43 
Saved me a lots of time! Thank you!
GeneralMy vote of 5memberts-swann12 May '11 - 3:25 
Nice one - saved me a lot of wailing and gnashing of teeth!
QuestionTIMEOUT PROBLEMmemberMember 775853116 Mar '11 - 5:38 
I juste perfectly follow this tuto but I have a TimeoutException...
Why ? An Idea ?
 
EDIT: I just notice that in server my URI was HTTPS and client HTTP...I put both on HTTPS and a new error appear...
 
-> The URI schema "HTTPS" is not valide, "HTTP" was attend...
 
HELP!
General"The security certificate for host 'localhost' does not match the name of the page you are trying to view"memberrondo_schwartz29 Jan '11 - 20:53 
Hey!
 
I followed the steps very carefully, yet something went wrong for me.
 
"The security certificate for host 'localhost' does not match the name of the page you are trying to view" - This is the message i get when trying to add a service reference to my service from VS 2008.
From my browser i can't event get to the service, and when my client tries to connect to the service he gets "Could not establish trust relationship for the SSL/TLS secure channel with authority ".
 
Can you advice on that?
 
Thanks,
 
Ron
Generalthanks for this.. but one question..memberRichard P S21 May '10 - 1:59 
Hi,
 
This article has been really useful. I've managed to get my 'secured' WCF client-server application working on my machine.
 
However I now need to apply this to our production box. The box already has a certificate installed, so is it just a matter of getting the hash-key thing from the 'certificates' plug-in screen, and using that to do a httpcfg command ?
 
The existing certificates are attached to IIS, so do I need to create some new ones based on the root certificate ?? Or can I just use one of the existing ones ? I'm new to certificates, so don't really know how they work.
 
Any advice would be massively appreciated.
 
Cheers
Rich.
GeneralMore service detail please.memberSteve Cav18 May '10 - 18:16 
I started again from scratch. I'm now trying to add a service reference to the client, but it gives the error:
---------------
Metadata contains a reference that cannot be resolved: 'http://localhost:49402/TLService.svc'.
Content Type application/soap+xml; charset=utf-8 was not supported by service http://localhost:49402/TLService.svc. The client and service bindings may be mismatched.
The remote server returned an error: (415) Unsupported Media Type.
---------------
For a start, why is it saying http not https? I'm using your exact config. My cert is tied to port 49402.
My service proj is a WCF Service library (runs ok with no SSL on it). Should it be a web site instead?
GeneralRe: More service detail please.memberanarchistic18 May '10 - 22:59 
It looks like you are attempting to use the VS 2008 automatic service host effort thing, or IIS (Im deducing this from the .svc endpoint). Either way, neither of those will work with the article Ive written. The IIS option because there is more (fun!) setup to be done in IIS, the VS host because it doesn't take into account the custom bindings.
no worries, mate.

GeneralRe: More service detail please.memberSteve Cav19 May '10 - 13:10 
Thanks. So how should I host it?
GeneralRe: More service detail please.memberanarchistic19 May '10 - 22:31 
I would go with a custom host, just to start out and get it all working.
 
At that point you can then start experimenting with whichever hosting option you actually want to use. Ive got an article on "self-hosting" if you click on my username.
no worries, mate.

General.AddressAlreadyInUseExceptionmemberSteve Cav17 May '10 - 15:04 
I have a service that was working fine unsecured.
 
I followed all these steps. As soon as I try to use https I can't run my service anymore, and get
System.ServiceModel.AddressAlreadyInUseException: HTTP could not register URL https://+:8731/TLService/Service1/. Another application has already registered this URL with HTTP.SYS. ---> System.Net.HttpListenerException: Failed to listen on prefix 'https://+:8731/TLService/Service1/' because it conflicts with an existing registration on the machine.
 
If I do a netstat, nothing's using that port.
Is there another place I need to change http to https??
GeneralRe: .AddressAlreadyInUseExceptionmemberanarchistic18 May '10 - 12:43 
Hey,
 
To be honest, the only time Ive ever seen that exception (and I assume you receive it when you try to invoke Open on the host) is when you have another instance of your service running already (or at least, another service on the machine:port combination you have specified) in another instance of VS or in a console window or something.
no worries, mate.

General2nd makecert fails for mememberpatc4 May '10 - 5:22 
I was able to make everything work in dev. Now I'm trying to put into production. I can create the root certificate on our live backend box. But when I run the 2nd makecert, it just outputs the basic parameters usage making it seem like I'm not supplying the correct parameters. I quadrupled checked everything, even retyped the line. I listed the basic and extended parameters and they're all there. I also copy and pasted the working line from the working dev box and just updated the names. I also copied over the exact makecert.exe I was using on the dev box. I just can't get the 2nd makecert command to run.
 
Any ideas?
GeneralRe: 2nd makecert fails for me - Figured it outmemberpatc4 May '10 - 8:21 
I did NOT have the same version of makecert.exe . The version that works for me is 5.131.3790.0
Generalstill facing issue. please help .. please reply ASAPmembersehariqbal1 Feb '10 - 5:04 
I did all you steps and got this error when try to run from IIS:
 
No protocol binding matches the given address 'https://simsltd-e2eee48:10081/WCFServiceCertificate3'. Protocol bindings are configured at the Site level in IIS or WAS configuration.

 
and when i try to run with url
https://simsltd-e2eee48:10081/WCFServiceCertificate3
the page can't be display error occur.
 
can you please help i am stuck in to this issue from last 3 days. plz help.
Then i actually have to more to Transport Certificate Service creation and my project is stuck just coz of this certificate issue
GeneralRe: still facing issue. please help .. please reply ASAPmembersehariqbal1 Feb '10 - 5:06 
Do i have to configure IIS for SSL as well ???
GeneralRe: still facing issue. please help .. please reply ASAPmemberanarchistic1 Feb '10 - 7:29 
Using WCF with IIS is a completely different topic. If you want to use SSL in IIS there are a whole bunch of other steps you would need to perform, IIS has its own implementation of SSL and would essentially wrap your communication in its own SSL implementation. Which is why I used a "Self-hosted" service.
 
Also note that if you are using any other binding other than http in IIS, you will need to go through the (painful) setup of WAS in IIS to support TCP.
 
no worries, mate.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 1 Mar 2008
Article Copyright 2008 by anarchistic
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid