Monday, February 28, 2011

Using X509 Certificates with WCF

I can't say whenever I need to use X509 certificates I've found it easy and straight forward. But unless you have the luxury of using Https you should be using X509 certificates to encrypt the message payload. I have also heard a strong argument to encrypt the message content even under https. Https encryption can be stripped at a firewall or proxy appliance (a few years ago I saw a Microsoft ISA solution that did). So in between the proxy server and the destination service server its unencrypted; not a big risk but nonetheless.

During development you can generate your own X509 certificates using the Makecert Visual Studio command line tool. (Its like Bear Grylls saying insects are a better source of protein and you can live off them; you can but it tastes like....) So a better tool is PluralSight's Self-Cert. In production ideally it would be best to have a certificate issued by a trusted certificate authority like Thwart or Verisign. But sometimes spending the money on a fully trusted certificate might be overkill. I am currently writing an intranet based service component using TCP/IP, so self-generated certificates should be fine. Web Browsers will give ugly warning errors for unofficial self-generated certificates, but given I'm writing both the consumer and the service it will be fine. You wouldn't get away with this in a Silverlight scenario.

Adding a X509 certificate to your service is pretty easy once you know where and how. The first step is to generate one using MarkCert or Self-Cert. Using Self-Cert is easier, and at the same time it registers it in the Windows Certificate store. If it is not registered in the Certificate store it won't do its job.

Secondly, simply add it to the app.config:

<?xmlversion="1.0"?><configuration><system.serviceModel><services><servicename="Service.CalculatorService"behaviorConfiguration="CalculatorServiceBehavior"><endpointbinding="netTcpBinding"bindingConfiguration="Binding1"contract="Service.ICalculator"name="NetTcpEndpoint"><identity><dnsvalue="TestKeyAuthentication"/><!-- This matches the x509 certificate name --></identity></endpoint><endpointaddress="mex"binding="mexTcpBinding"name="NetTcpMetadataPoint"contract="IMetadataExchange"/><host><baseAddresses><addbaseAddress="net.tcp://localhost:9000/service/UserNamePasswordValidator"/></baseAddresses></host></service></services><bindings><netTcpBinding><bindingname="Binding1"><securitymode="Message"><messageclientCredentialType="UserName"/></security></binding></netTcpBinding></bindings><behaviors><serviceBehaviors><behaviorname="CalculatorServiceBehavior"><serviceMetadatahttpGetEnabled="false"/><serviceDebugincludeExceptionDetailInFaults="false"/><serviceCredentials><!-- The serviceCredentials behavior allows one to specify a custom validator for username/password combinations. --><userNameAuthenticationuserNamePasswordValidationMode="Custom"customUserNamePasswordValidatorType="Service.CustomUserNameValidator, Service"/><!-- The serviceCredentials behavior allows one to define a service certificate. A service certificate is used by a client to authenticate the service and provide message protection. This configuration references the "localhost" certificate installed during the setup instructions. --><serviceCertificatefindValue="TestKeyAuthentication"storeLocation="LocalMachine"storeName="AuthRoot"x509FindType="FindBySubjectName"/></serviceCredentials></behavior></serviceBehaviors></behaviors></system.serviceModel></configuration>

The key element is <serviceCertificate />. The Findvalue attribute is the "common name" of the certificate (Self-Cert calls it distinguished name).

For a full working example see this post.

If your using callbacks (aka duplex) then you should add the client certificate to the service app.config as well. This is so when the service is calling the client back on the callback contract it can encrypt the message. (In an ordinary request/response call the client has its own certificate and also the service's certificate from proxy generation time so it can encrypt, but the service has no knowledge of the client upfront).

<clientCertificate><certificatefindValue="TestKeyAuthentication"storeLocation="LocalMachine"storeName="AuthRoot"x509FindType="FindBySubjectName"/></clientCertificate>Add this element under the <serviceCertficate /> element as a sibling to it.

I'll update this post if I run into any problems with deploying using self generated certificates.

No comments:

Post a Comment