Tuesday, June 3, 2008

Using a Client Certificate to Restrict Access to a WCF Service Method

This post is based on this article.

Service Configuration

We are going to configure our service for Transport and Message Security with a client certificate, and we are going to add an attribute to a service method to restrict access only to clients that present the correct certificate. We will use the WSHttpBinding. The port is already configured for https from this post.

First configure the binding:

WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;


To designate the certificate that is permitted to access the service method, you will need to know the subject name and thumbprint of the certificate. Import System.Security.Permissions and decorate the method with the following attribute:

[PrincipalPermission(SecurityAction.Demand,
Name = "CN=joeuser.example.com; ddf5066afceb34a95ff75795ced345ff549464202")]


Here the Name parameter is equal to the certificate Subject followed by a semi-colon, a space, then the certificate Thumbprint. Multiple attributes can be added to support more than one client certificate. The final configuration option is to set the mode on the service to map the certificate information to this format (Subject; Thumbprint). This is done through the ServiceBehaviorAuthorizationMode (set it to UseAspNetRoles). You will need to import System.ServiceModel.Description.

ServiceHost host = ...
ServiceAuthorizationBehavior b = host.Description.Behaviors.Find();
b.PrincipalPermissionMode = PrincipalPermissionMode.UseAspNetRoles;


Client Configuration

Now we need to tell the client which certificate to present to the service. The same method is called whether you are using a Client Proxy or have created a ChannelFactory. I will show the example using a ChannelFactory. This must be done before creating the Channel.

factory.Credentials.ClientCertificate.SetCertificate(
System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
System.Security.Cryptography.X509Certificates.StoreName.My,
System.Security.Cryptography.X509Certificates.X509FindType.FindBySerialNumber,
"1a2fa58b000000003a5c");


In this case, I am looking it up by Serial Number, but the certificate can be found by Thumbprint, Subject Name, or a number of other fields. Again, use the MMC Certificate Snap-in to look up the information.

That's all there is to it.

1 comment:

Unknown said...

Works great however it doesn't seem to matter which certificate the client passes as long as it's issued by a trusted CA. How do I secure the operation (or better yet, the entire service) to a single certificate?