I spent some time today figuring out how to create a WCF client for a service that requires client-side authentication via certificate. In the end, the fix was simple, but it took me a while to get there so I figured I'd share it out in the hope that future searches (like mine) will lead someone here and to the solution. There are a two parts to this - one on the service side, the other on the client side. I'll assume you already have the required certificate installed on the client machine.

Server side

You need to allow the service to advertise its capabilities first. At least for my service, when it was secured with client-side certifications as a requirement, this was disabled. You need to do two things to remedy this:

  • Add a new endpoint that provides configuration information, in my case a MEX (Metadata EXchange) endpoint.
  • Enabling retrieval of this metadata as a behaviour of my service. 

Here's what my web.config looked like after I made the modifications. Changes in red. Bits in blue are where you need to fill in details of your application.

<configuration>

 <system.webServer>
  <security>
   <access sslFlags="Ssl, SslNegotiateCert, SslRequireCert"/>
  </security>
 </system.webServer>

 <system.serviceModel>
  <services>
   <service name="MyServiceName" behaviorConfiguration="MyServiceBehavior">
    <endpoint binding="wsHttpBinding" bindingConfiguration="wsHttpSecureBinding" contract="MyServiceInterface"/>
    <endpoint contract="IMetadataExchange" bindingConfiguration="wsHttpSecureBinding" binding="wsHttpBinding" address="mex" />
   </service>
  </services>

  <behaviors>
   <serviceBehaviors>
    <behavior name="MyServiceBehavior">
     <serviceCredentials>
      <clientCertificate>
       <authentication certificateValidationMode="Custom" customCertificateValidatorType="MyCustomValidator, MyCustomValidatorAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456789abcdef"/>
      </clientCertificate>
     </serviceCredentials>
     <serviceMetadata httpGetEnabled="true" />

    </behavior>
   </serviceBehaviors>
  </behaviors>

  <bindings>
   <wsHttpBinding>
    <binding name="wsHttpSecureBinding">
     <security mode="Transport">
      <transport clientCredentialType="Certificate"/>
     </security>
    </binding>
   </wsHttpBinding>
  </bindings>
 </system.serviceModel>
</configuration>

 

Client side

I use scvutil.exe to generate my WCF clients, and I wanted to continue doing it this way. It turns out that svcutil will read a config file placed alongside it, and this is how you specify which certificates to use when hitting the service. Without this, IIS will reject the request and you won't be able to get the service description to generate a client. Just create a svcutil.exe.config file next to svcutil.exe (not in your current working directory) and the configuration should take effect. Alternatively, you can pass in a config file to svcutil by calling svcutil.exe http://example.com/myService.svc /config:myconfig.config

 

Here's what my svcutil.exe.config looked like. You'll probably need to change the parts in blue to suit your certificate and its location.

<configuration>
  <system.serviceModel>

    <client>
      <endpoint behaviorConfiguration="ClientCertificateBehavior" binding="wsHttpBinding" bindingConfiguration="MyBinding" contract="IMetadataExchange" />
    </client>

    <behaviors>
      <endpointBehaviors>
        <behavior name="ClientCertificateBehavior">
          <clientCredentials>
      <clientCertificate findValue="0123456789abcdef0123456789abcdef01234567"
       x509FindType="FindByThumbprint" storeLocation="CurrentUser"
       storeName="My"/>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>

 <bindings>
      <wsHttpBinding>
        <binding name="MyBinding">
          <security mode="Transport">
            <transport clientCredentialType="Certificate" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>

  </system.serviceModel>
</configuration>

There are a number of different ways to define which certificate to use, but my preference is by thumbprint as there is certainly no ambiguity then.

Now when you run svcutil, it will look up the specified certificate and use it when attempting to communicate with the IMetadataExchange service to retrieve your service description.

 

Conclusion

Hopefully you're now up and running! I can also recommend How to: Use Certificate Authentication and Transport Security in WCF Calling from Windows Forms for a more in-depth and end-to-end guide for how to achieve this, but I hope this guide has been sufficiently terse while giving you the essential information. Please let me know in comments if you need any help.

-Stephen