Webhosting a WCF service over Https (with your own Certificates)
Disclaimer: The information contained in this posting is not intended for production environments. But it should effectively mimic a production environment.
A while back, I posted a lengthy how-to for setting up a selfhosted WCF service that uses Transport level security (https.) I found that posting useful, when trying to remember how to set that up, but recently I was helping a colleague set up a webhosted service using https. Now, for most cases, testing this with your own self-signed certificate works fine, but if you ever try browsing to the service cross-machine, you'll notice the certificate isn't trusted. In this posting, I'll review how to create the chain-signed certificate and how to export the trusted root certificate to a "client" machine.
Setting up the service
For reference, I'll be using the service implementation in the "Selfhosting a WCF service over Https" posting.
The difference is that I've changed the project type to a class library and added a HelloWorldService.svc file with the following content:
<%@ ServiceHost Debug="true" Service="HelloWorldWebhost.HelloWorldService" Language="C#" Factory="System.ServiceModel.Activation.ServiceHostFactory" %>
And I added a Web.Config with the following content:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="MyBinding"/>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="metadata">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="HelloWorldWebhost.HelloWorldService" behaviorConfiguration="metadata">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="MyBinding" contract="HelloWorldWebhost.IHelloWorld"/>
</service>
</services>
</system.serviceModel>
</configuration>
Finally, I changed the output path from "bin\Debug\" to "bin\" in the project properties. Next, I open IIS (IIS7, in my case), right click "Default Web Site" and select "Add Application." The virtual path (Alias) is AdHocHelloWorld and the physical path is the project folder I was using in VS. Next, I right-click the application in IIS and select "Edit Permissions" to give IIS_IUSRS access to the folder. Finally, I switched to Content View in IIS and browsed to the .svc file to make sure I got the service activation page.
Modifying for https
To change the service to use Transport security, all I have to do is change the binding:
<basicHttpBinding>
<binding name="MyBinding">
<security mode="Transport"/>
</binding>
</basicHttpBinding>
Now, if I just refresh the service in IE, I get the following error:
Could not find a base address that matches scheme https for the endpoint with binding BasicHttpBinding. Registered base address schemes are [http].
This is where you need to add a binding in IIS for https. And to do that, you need a certificate. Looking at my old blog post, I run the following two commands on my machine:
makecert.exe -sk RootCA -sky signature -pe -n CN=<machineName> -r -sr LocalMachine -ss Root MyCA.cer
makecert.exe -sk server -sky exchange -pe -n CN=<machineName> -ir LocalMachine -is Root -ic MyCA.cer -sr LocalMachine -ss My <certificate path>
Of course, I changed <machineName> to be my machine's fully-qualified machine name. And the <certificate path> was MyAdHocWebhostHttpsCert.cer.
Next, I right-click Default Web Site, select Edit Bindings… Under Type, I select https. The SSL certificate dropdown now is populated with the cert I created in the above steps. These are labeled by "CN" parameter used.
Now, when I refresh my IE page to the service, it activates just fine. In fact, if I look at the wsdl, I see the address to the service uses the https scheme now.
Setting up the remote client machine
If I now browse to the service (on https) using IE on a remote machine, I get the following (after remembering to allow Http and Https publishing through my firewall):
There is a problem with this website's security certificate. |
The security certificate presented by this website was not issued by a trusted certificate authority. The security certificate presented by this website was issued for a different website's address. Security certificate problems may indicate an attempt to fool you or intercept any data you send to the server. |
You can make your client machine trust the server's certificate by exporting the trusted root certificate from the server machine and importing it on the client machine. Here's how to do this. On the server machine, run mmc.exe. File -> Add or Remove Snap-ins -> Certificates -> Add. Select Computer account (Local). Click Ok and unfold down to Trusted Root Certification Authorities. In there, you should find the certificate that was installed by the first makecert.exe command. The "Issued To" and "Issued By" are both the machine name. Right click it and select Export. For this test, I'm not exporting the private key. I select the defaults on everything else and pick a file name at the end, placing it in my documents folder.
Next, I copy the certificate to the client machine. This is sort of mimicking how Verisign installs their trusted root certificate on all windows machines. Of course, providers then need to ask Verisign to issue them a certificate to secure their web services with. But more on that topic is probably widely available elsewhere.
On the client machine, simply double-click the certificate that was copied. When the wizard asks which certificate store to install it to, select Trusted Root Certification Authorities. Everything else, just use the defaults. Now, if you look at the certificates on the client machine (mmc.exe - Certificates) you should see the certificate under Trusted Root Certification Authorities. Further, refreshing the IE window on the client machine shows there is no longer a problem with the certificate.
Happy testing!
Comments
Anonymous
March 22, 2011
I am trying to follow your series, but when I try to implement the Self-Hosting a WCF Service over Https, I get the following error: The HTTP request was forbidden with client authentication scheme 'Anonymous'. Can you help me?Anonymous
April 01, 2011
Sounds like your client's binding wasn't configured to use transport-level security. How is the client binding configured?