views:

390

answers:

1

I am using C#/WCF. I have a web service which shall be invoked by the client. This is the service definition:

And this is the binding

If I understand this correctly, the messages sent from server to client are encrypted with a certificate. Currently I am still working with developer certificates. I created a root certificate, a certificate revokation list and a key on the server.

I am installing the client with Windows Installer and I have a custom install action to install the certificates.

The following code shows how the certificates are added to the store

        Stream manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClientCertificates.MyRoot.cer");
        byte[] buffer = new byte[((int)(manifestResourceStream.Length - 1L)) + 1];
        manifestResourceStream.Read(buffer, 0, (int)manifestResourceStream.Length);
        manifestResourceStream.Close();

        var cert = new X509Certificate2(buffer);
        var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadWrite);
        store.Add(cert);
        store.Close();

        /*
        // The CRL is also needed, no idea why
        manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClientCertificates.MyRoot.crl");
        buffer = new byte[((int)(manifestResourceStream.Length - 1L)) + 1];
        manifestResourceStream.Read(buffer, 0, (int)manifestResourceStream.Length);
        manifestResourceStream.Close();
        cert = new X509Certificate2(buffer);
        store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadWrite);
        store.Add(cert);
        store.Close();
         * */

        // This is the key 
        manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClientCertificates.MyTestServer.cer");
        buffer = new byte[((int)(manifestResourceStream.Length - 1L)) + 1];
        manifestResourceStream.Read(buffer, 0, (int)manifestResourceStream.Length);
        manifestResourceStream.Close();

        cert = new X509Certificate2(buffer);
        store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadWrite);
        store.Add(cert);
        store.Close();

I have now two behaviors: Installing the certificates work, but when I call the web service I get a SecurityNegotiationException. When I add the Certificate Revocation List manually, the communication with the server works. When I try to do it programmatically (see code above) it does not work. I get a "Could not find requested object" exception.

I tried to use different stores but with no success.

I have two questions: a) Why do I need the CRL on the client? b) If I need it, how can I install it programmatically? Where is my mistake above?

Thanks for your help, Kay

A: 

Generally, the CRL has to be available online and downloadable from the revocation URL specified in the server cert. I don't know if there's an out-of-band mechanism for getting it, but even if there was, it'd kinda defeat the purpose (allowing clients to discover that a server cert has been compromised/revoked). That said, a CRL is really overkill for self-signed certs unless you're using the cert for real mutual authentication and you're worried about the key being compromised (in which case, buy a commercial cert and let them deal with it).

If you can't get a cert generated without a revocation URL, I'd recommend disabling the client check of the CRL altogether unless you really need it. You can do this by adding the following to the webservice client's app.config:

  <system.net>
    <settings>
      <servicePointManager checkCertificateRevocationList="false"/>
    </settings>
  </system.net>

If you're using WCF, you may need to wire it up with a clientCredentials endpointBehavior, under serviceCertificate->revocationMode:NoCheck instead.

nitzmahone