views:

352

answers:

2

I have a WCF client/service app that relies on secure communication between two machines and I want to use use x509 certificates installed in the certificate store to identify the server and client to each other. I do this by configuring the binding as <security authenticationMode="MutualCertificate"/>. There is only client machine.

The server has a certificate issued to server.mydomain.com installed in the Local Computer/Personal store and the client has a certificate issued to client.mydomain.com installed in the same place. In addition to this the server has the client's public certificate in Local Computer/Trusted People and the client has the server's public certificate in Local Computer/Trusted People.

Finally the client has been configured to check the server's certificate. I did this using the system.servicemodel/behaviors/endpointBehaviors/clientCredentials/serviceCertificate/defaultCertificate element in the config file.

So far so good, this all works. My problem is that I want to specify in the server's config file that only clients that identify themselves with the client.mydomain.com certificate from the Trusted People certificate store are allowed to connect.

The correct information is available on the server using the ServiceSecurityContext, but I am looking for a way to specify in app.config that WCF should do this check instead of my having to check the security context from code.

Is that possible? Any hints would be appreciated.

By the way, my server's config file looks like this so far:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="MyServer.Server" behaviorConfiguration="CertificateBehavior">
        <endpoint contract="Contracts.IMyService" binding="customBinding" bindingConfiguration="SecureConfig">
        </endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost/SecureWcf"/&gt;
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="CertificateBehavior">
          <serviceCredentials>
            <serviceCertificate storeLocation="LocalMachine" x509FindType="FindBySubjectName" findValue="server.mydomain.com"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <customBinding>
        <binding name="SecureConfig">
          <security authenticationMode="MutualCertificate"/>
          <httpTransport/>
        </binding>
      </customBinding>
    </bindings>
  </system.serviceModel>
</configuration>
+1  A: 

Check out the WCF Security Guidance page on Codeplex - excellent and very useful stuff!

In particular, check out the How-To's and even more specifically the

How To – Use Certificate Authentication and Message Security in WCF calling from Windows Forms

It explains in great detail how to set up a WCF service which requires its clients to present a valid certificate, and how to check that. If you want to allow only a single client, deploy that certificate only specifically to that one single client.

Hope this helps!

marc_s
That's a good example, but it only checks that the client has a valid certificate - any valid certificate. Doesn't it? If an unwanted client has the public part of the root certificate so that it can verify the server's certificate and the unwanted client also has its own certificate issued by say VeriSign, then it can connect, can't it? That is exactly what I want to stop.
Johan Levin
@Johan: depends on what you check for in your certificate. I'm sure you can add some kind of a serial number or something to it, and as long as that key isn't present (if someone manages to steal just the public parts of the certificate), you won't accept it.
marc_s
The certificate has both a distinguished name, a serial number and a thumbprint. I know how to check all three in code. I was looking for a way to do it declaratively from app.config instead of imperatively from code. But I think I had authentication and authorization mixed up when I asked this question. A better approach may be to authenticate all trusted certificates and then add some sort of authorization on top of that. Maybe I have to plug in some custom code to e.g. map a certifiate to a role somehow and then rely oh role based authorization.
Johan Levin
A: 

There doesn't appear to be a way to do what I want using web.config.

I ended up adding a behavior with this tag:

<clientCertificate>
  <authentication certificateValidationMode="PeerTrust" trustedStoreLocation="CurrentUser" revocationMode="NoCheck"/>
</clientCertificate>

And then add the client's certificate to the "trusted people" certificate store of the user that the server runs as.

Johan Levin