How to access ClientCertificate in a host agnostic manner

 When you choose to use Client Certificate as your client credential over SSL, you want to retrieve the x509 Certificate on the server side to do authorization. It is very simple with the latest Web API bits. If you have access to the request message anywhere in the pipeline, such as message handler, action filter or action. you can simply call the extension method shown below. Please be careful that the GetClientCertificate method might return null if the client does not send an valid certificate.

Now getting the client certificate is easy, but how to set it up correctly on self host and web host is not that easy. The following instructions walk you through the process step by step.

Web Host

Step 1: go to the IIS manager, SSL setting, check require client certificate

Step 2: register a custom message handler and verify that the client certificate is something you expect.

Code Snippet

  1. public class CustomCertificateMessageHandler : DelegatingHandler
  2.     {
  3.         protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  4.         {
  5.             X509Certificate cert = request.GetClientCertificate();
  6.             if (cert != null)
  7.             {
  8.                 if (cert.Subject.Contains("Some Name you are expecting"))
  9.                 {
  10.                     Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(cert.Subject), new[] { "Administrators" });
  11.                 }
  12.             }
  13.  
  14.             return base.SendAsync(request, cancellationToken);
  15.         }
  16.     }

Step 3: register the custom message handler.

Code Snippet

  1. // Register a message to turn the client cert to a admin role
  2.             GlobalConfiguration.Configuration.MessageHandlers.Add(new CustomCertificateMessageHandler());

 

Step 4: Write a RequireAdminAttribute

Code Snippet

  1. public class RequireAdminAttribute : AuthorizationFilterAttribute
  2.     {
  3.         public override void OnAuthorization(HttpActionContext context)
  4.         {
  5.             // do authorization based on the principle.
  6.             IPrincipal principal = Thread.CurrentPrincipal;
  7.             if (principal == null || !principal.IsInRole("Administrators"))
  8.             {
  9.                 context.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
  10.             }
  11.         }
  12.     }

Step 5: Add the RequireAdminAttribute to the action.

Code Snippet

  1. public class HomeController : ApiController
  2.     {
  3.         [RequireAdmin]
  4.         public HttpResponseMessage Get()
  5.         {
  6.             return new HttpResponseMessage(HttpStatusCode.OK)
  7.             {
  8.                 Content = new StringContent("Default User")
  9.             };
  10.         }
  11.  
  12.         public HttpResponseMessage Post()
  13.         {
  14.             return new HttpResponseMessage(HttpStatusCode.OK)
  15.             {
  16.                 Content = new StringContent("User Posted")
  17.             };
  18.         }
  19.     }

Now your GET action can only be accessed with the administrators.

Self Host

Step 1: Tell the WCF transport, I want to client to send certificate over SSL.

Code Snippet

  1.             // client cert
  2.             config.ClientCredentialType = HttpClientCredentialType.Certificate;

Step 2: Write some client code to pick a certificate to send

Code Snippet

  1.                 WebRequestHandler handler = new WebRequestHandler();
  2.                 handler.ClientCertificateOptions = ClientCertificateOption.Manual; // this would pick from the Current user store
  3.                 handler.ClientCertificates.Add(GetClientCertificate());
  4.                 handler.UseDefaultCredentials = true;
  5.                 HttpClient client = new HttpClient(handler);

Implement the GetClientCertificate to retrieve a certificate programmatically.

Then repeat step 2-5 from the web host section to complete the experience.

Hope this helps.

Comments

  • Anonymous
    December 18, 2012
    Does the certificate have to be a valid certificate?

  • Anonymous
    January 31, 2014
    If you require SSL and a client cert why would GetClientCertificate() result in null? IIS would not allow the request to continue.

  • Anonymous
    May 27, 2014
    Is there any sample code that shows how a javascript or .NET client can send a certificate as part of an http request.