2
votes

I am trying to setup a WCF service to use a Certificate for Authenticating the client. I have read tons of posts on how to create the certificate, and I have been able to do so (finally).

I am installing the Cert Authority and the Cert on a server that runs Windows 2008 R2. When I open the MMC Certificates Snap-in, I choose Computer Account. Is this correct? I am doing this because my WCF service will run in a Windows Service, and will be running even when no user's are logged in. But admittedly, I don't know what the difference is between the three options:

  1. My user account
  2. Service account
  3. Computer account

Once the snap-in loads, I import the Authority Cert into Trusted Root Certification Authorities. Then, I import the cert into Trusted Publishers. I don't encounter any errors when doing this. When I do the import, of both the Authority Cert and the Cert signed by that authority, I don't make any reference to the .pvk file. It is my understanding that the private key is embedded in either the cert or the authority cert. Here are the commands I use to create each cert:

MakeCert.exe
  -n “CN=InternalCA”
  -r
  -sv InternalCA.pvk InternalCA.cer
  -cy authority


MakeCert.exe
  -sk InternalWebService
  -iv InternalCA.pvk
  -n “CN=InternalWebService”
  -ic InternalCA.cer InternalWebService.cer
  -sr localmachine
  -ss root
  -sky exchange
  -pe

Notice I used -ss root. I have seen many posts using -ss My. I don't really understand what the difference is or when it is appropriate to use each value.

My WCF service runs on this machine inside a Managed Service (Windows Service). When I start my windows service, which hosts the WCF service, it crashes immediately and a seemingly common error is reported in the event viewer:

System.ArgumentException: It is likely that certificate 'CN=TempCertName' may not have a private key that is capable of key exchange or the process may not have access rights for the private key

I have found posts that say I need to grant permissions to the user running the service to the key.

This one seems to be a popular answer here on stackoverflow: Grant access with All Tasks/Manage Private Keys

But I don't have the option of All Tasks/Manage Private Keys

But I'm not clear on how to do that. But also, the service is running under my domain account, which is an administrator and is also the same user that installed the cert.

Please Help :)

2
I'm gonna see if this helps: stackoverflow.com/questions/893336/…essedbl

2 Answers

1
votes

Here's the best link that should help you get your self-hosted SSL WCF service to work with your own custom CA/certificates: SSL with Self-hosted WCF Service.

After you get it working from the guide above, you may want setup your service programatically to use the right certificate during installation-time.

I find that verifying my HTTP.SYS configuration with the HTTPCfgUI tool to be easier than via the command line httpcfg/netsh commands.

Next, if you still encounter errors, you can debug further using WCF Tracing. Additionally, you should turn on WCF Message Tracing as well. You can trace the .NET network stack too, if the WCF tracing doesn't provide enough information.

You can test if your certificate/CA pair on the service is working by hitting your service URL in a browser on another machine. It should first state that the certificate is invalid. Then, import the CA on the machine into the trusted root, and hit the service URL again. This time it should display the service description page as usual, with no warnings.

1
votes

I think you are close. Here are some suggestions:

  1. Make sure you are getting a private key attached to your certificate in the second step. You have to run the command in an elevated process -- even if you have Admin privileges, you have to, for example, right-click and "Run as Administrator" to start the command shell you use for this command. Otherwise, you won't get the private key into the localMachine store.

  2. I would use -ss my and put the certificate (with private key) into the Personal store. Here I see this:

    cc.ClientCredentials.ClientCertificate.SetCertificate( StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "contoso.com");

and so wherever you are pointing to in your equivalent, put the certificate there.

  1. You don't need to import the private key of the CA cert (the first one you created). That's only kept around to sign more certificates with MakeCert. You will need a copy of that CA certificate (without the private key!) on the client that is connecting in, otherwise the client won't be able to validate the InternalWebService certificate.

  2. You don't really need the CA certificate locally on the server machine, because it will only be needed by the client. But it doesn't hurt, and would be needed if anything on the server connects to the service locally. Also, it makes the InternalWebService certificate look good on the MMC snap-in. You can try with and without the CA in the Trusted Root store and you'll see what I mean. But in any case, I would not put the private key of the CA into the local computer store.

  3. Check the private key permissions for InternalWebService from the MMC snap-in (right-click on the certificate, then Tasks, Manage Private Keys...) If you import under a different user account than the service runs under, then certainly it won't have access yet, and you have to go give it access. Otherwise the service will get the certificate, but it will appear that the certificate does not have a private key.

To summarize:

  • Run with Admin privileges to make sure the private key of InternalWebService is really getting into the certificate store. (You'll see a little key on the certificate in the MMC snap-in, and right-clicking on the certificate will have an option "Manage Private Keys..." which is not present if there is no private key attached.)

  • Put the InternalWebService in the place where you Web service is looking for it. I would guess "Personal" (a.k.a. "my") but you know where that is in your service config. Whether current user or local machine, look in your config.

  • Give access to the InternalWebService certificate private key.

  • Put the CA certificate -- without its private key -- under Trusted Root, and you'll need to put that on the client as well (or otherwise have the client accept an "untrusted certificate" at its end.)