Embedding Certificates When Using WCF Custom Security Tokens.

Microsoft’s Windows Communication Framework (WCF) is an amazingly robust framework for web services and general internet messaging. However, as soon as you step outside the “default” box laid out for you by MS, things can get pretty hairy. One example is using a set of custom tokens for message authentication. The built-in WCF authentication schemes aren’t bad, but sometimes you need something more. This can be done by extending somewhere in the neighborhood of 7 classes to control, serialize, and authenticate a custom token of your own.

Now, anyone who has attempted to use custom security tokens will quickly find that WCF requires you to use an x509 certificate to encrypt your tokens. After all, sending them plain-text would be a big security flaw, and we all need saved from ourselves, right? 🙂

The first time I did this, it wasn’t a big deal. For development, I just made a self-signed certificate that contained the public cert and the private key (in a .pfx file), and installed it on all the developer’s machines. The WCF client and server would both pick it up (since they were running locally) and life was grand… until it came time to roll out the real product to users. Our system was set up in a way that there was a single central server, hosting many WCF services, and a client .exe that had to be deployed to hundreds of client machines. Suddenly I had a problem; do I really want to install a certificate manually, or even through an installer, on hundreds of machines? The answer is no.

So, on to the real meat of the post; You can embed your certificate into your assembly as an embedded resource, and assign it to the WCF channels.

1) Assuming you already have your certificate saved to a file (i’ve tested this with a .cer and a .pfx file), just add it to your project. Then click once on the file, and in the Properties window, set the Build Action to Embedded Resource.

2) Somewhere in your code when the service or the client is being set up, you need to read the certificate from the embedded resources and turn it into an x509 certificate. There are multiple places that you can do this, and it will largely depend on the way your application is set up. You can read the embedded resource and turn it into an x509 certificate like this:

var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MyCert.cer");
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
var cert = new X509Certificate2(bytes, "certPassword");

Yep, that’s it… simple! Just change the manifest resource name in line 1 to be the name of your embedded resource, and the certificate’s password (if it has one) in line 4. The cert variable will then be your loaded certificate.

In practice, I just want to do this once on startup and keep the certificate around. To do that, I just save cert to a static variable and only reload it if it is null. If you do that, then remember to add a lock() { } section for thread safety!

3) Assign the x509 certificate to your channel. I’ve found that if you are implementing a custom security token, then you probably have a class that extends ServiceCredentials or ClientCredentials like this:

public class MyClientCredentials : ClientCredentials
{
    public MyClientCredentials() : base()
    {
    }
}

In the constructor for your Client or Service credentials, you can set the certificate by doing:

public class MyClientCredentials : ClientCredentials
{
    private static X509Certificate2 _cert;

    public MyClientCredentials() : base()
    {
        if(_cert == null)
            LoadCertificate(); // this would load the certificate into _cert, as in #2 above

        // this assumes that the client and server are both using the same certificate to sign messages.
        this.ClientCertificate.Certificate = _cert;
        this.ServiceCertificate.Certificate = _cert;
    }
}

4) Clean up the WCF configuration. If you had this working previously on certificates from the windows certificate store, then you probably had something like this in your WCF configuration:

<endpointBehaviors>
  <behavior name="MyEndpointBehavior">
    <clientCredentials type="MyClientCredentials, MyApp">
      <clientCertificate findValue="MyCert" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My" />
      <serviceCertificate>
        <defaultCertificate findValue="MyCert" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My" />
      </serviceCertificate>
    </clientCredentials>
  </behavior>
</endpointBehaviors>

Well, since the client credentials class (MyClientCredentials) is now setting its own client and server certificate in its constructor, we no longer need to specify them in the WCF configuration, so the above becomes:

<endpointBehaviors>
  <behavior name="MyEndpointBehavior">
    <clientCredentials type="MyClientCredentials, MyApp" />
  </behavior>
</endpointBehaviors>

5) Think security! The obvious big downfall to this method of embedding the certificates is that if someone gets a hold of your assembly, then they could potentially have your certificate. So, the safe thing to do is to only embed the public portion of the certificate (the .cer file) and not embed the private key. You could easily set up WCF to use an assembly embedded public certificate for the clients, but have the server still use the private key from the windows certificate store.

Also, it is a good idea to obscure the certificate password. In my above example I just used “certPassword”. This would not be a good practice, as a hacker would probably extract the strings from your assembly and try each, or just decompile it and see the password. You can add some level of security by breaking the password into multiple strings in different classes, combining them, and then maybe ROT13 or MD5 hash that to get the password, plus obfuscate your assembly.

Still though, remember that nothing is 100% secure. Using the ‘classic’ method of putting the certificate into the windows store, if someone obtained a copy of your assembly, then they probably had access to the machine it was on, in which case they could have just opened the Certificates MMC Snap-in and exported the certificate anyway.

Tagged with: , ,
Posted in Programming
5 comments on “Embedding Certificates When Using WCF Custom Security Tokens.
  1. Ghostly says:

    Very nice indeed ! Something of a security hit, but oh boy, I can see it eliminates a lot of stupidity with privileges and cert installation.

    I’ve built a bunch of heavy duty WCF services, all using message based x509 encryption – and the woes I have had installing it onto a diverse OS client base cannot be understated. Really ugly handling to cover all possible bases ( added fun when trying to install it on the fly from a click once app ). Whilst WCF does offer a kitchen sink toolbox for all communication type things, the cert handling to me feels unpolished and unloved, and is in practice a b!tch – your solution is a real nice and simple alternative.

  2. basti says:

    it’s a very nice post and i think a secure clear way. in Addition it is possible to encrypt your exe file with Special Tools like dotbundle or something else, it makes everthing a secure Level higher to hide your Import Password.
    But i have one question?: how you Setup the wcf Service exactly, cause in my case i just got an error. is it possible to get an example?!

    • rally25rs says:

      I had originally done this as part of a project at work, so I do not have an isolated code sample that I can share, but if I remember correctly, I just created the services using the template in the “Add new item” wizard in Visual Studio.

  3. Horacio SErrano says:

    This does not work and i have not found the answer ANYWHERE. I have a certificate, it works because the embebed code does work from our old app. now i need to make a new app but i cannot get the WCF to generate the freaking interface. 99% of the problems i find online and that nobody has an answer to, is how to consume the wsdl when it requires a certificate. installing it on the personal storage does not work, on the machine does not work, nobody knows how to get this done.

Leave a reply to Ghostly Cancel reply