Sergey Shishkin

on agile software development

WCF: IIS Certificate Authorization

Recently I became an “Indigo developer” for one week! One of the most exciting experiences for me was developing certificate-based authentication and authorization for IIS-hosted service. That was a real challenge since this topic is not covered in SDK documentation and even Google returns nothing relevant. One of many things I like in being developer is the challenging research I’m able to face. To face it under the pressure of time is extreme excitement. Hope now this post will help other developers who are looking for the solution of the same problem.

Transport Security

WCF by itself supports certificate-based transport security and a bunch of examples pretty easy to start with. I got “Transport Security” sample from SDK for implementing the solution. This sample enables transport security both on client- and service-side and the only thing for me to do was to get authorization into play.

Unfortunately, I could not find any opportunity to use ASP.NET-like configurable authorization with certificates as a user’ credentials, but IIS brings new possibilities for this task.

I enabled SSL in IIS for my web-site with the certificate installed with example and got rid of that certificate in service’s configuration file. Since IIS is responsible for the transport security, service is not required to have a certificate but in any case transport security should be enabled in its configuration file. There are no changes to be done in client’s configuration file to work with IIS over HTTPS instead of original service’s transport security and I was pleasantly surprised with such compatibility.

Authorization

According to the requirements I needed to deny access to the service for everyone except the predefined number of clients with pre-issued certificates. The easiest but not configurable way to do so was to check System.ServiceModel.ServiceSecurityContext object for a specific certificates on the service. I prefer not to use it because I believe that such things should be implemented by professionals somewhere deep in the infrastructure and configured declaratively.

Declarative way to deny access on the service-side is to define a special attribute on the service’s edge method. Such an attribute is the System.Security.Permissions.PrincipalPermissionAttribute which can demand a specific role, principal name, or just check if the caller is authenticated. Since PrincipalPermission attribute does not support certificates directly one more step is required to achieve the goal to authorize callers using certificates.

IIS Certificates Mapping

One of the great features of IIS is the IIS Certificates Mapping which maps pre-issued certificates to the corresponding Windows accounts. IIS supports one-to-one and many-to-one mappings. I created Windows account for a client’s certificate and configured IIS virtual folder to map the certificate to that account.

Another important thing is to disable all authentication types, except anonymous. It will make IIS to treat all users without certificates and with certificates which are not mapped as anonymous while only users with mapped certificates will be authenticated with corresponding Windows accounts.

To make configuration easier I set PrincipalPermissionAttribute on all public service’s methods to allow only authenticated callers without any specific role or principal name. After that the IIS Certificates Mapping Manager becomes the single entry point to configure clients’ authentication.

Deployment

This is a “How-To” section of this post. I described the general idea of the solution above and here I’m going to dig into details of certificates deployment.

The certificate deployment is based on the setup.bat script from the “Transport Security” SDK’s sample but with some changes. To test the implemented authorization I generated additional client certificate, which I did not mapped to any Windows account.

In addition to certificate creation one needs to copy a certificates’ root authority called “Root Agency” from the “Intermediate Certification Authorities” to the “Trusted Root Certification Authorities”. Please, note that “Root Agency” is the default authority and copying it to the trusted authorities is a security issue which will make the machine to trust potentially not-trusted certificates. This should be done only for development purposes. In the production custom certification authority should be used to generate certificates.

certmgr.exe -add -s -r LocalMachine CA -c -n "Root Agency" -s -r LocalMachine Root

certmgr -del -s -r LocalMachine Root -c -n "Root Agency"

You may also want to copy one of the clients’ certificates to a file to use lately to configure certificate mapping.

certmgr.exe -put -s -r LocalMachine TrustedPeople -c -n %CLIENT1_NAME% client.cer

After certificates are installed IIS SSL and certificates mapping could be configured. To do so open IIS Manager and browse to the web site. On the web site’s properties dialog open the “Directory Security” tab and click “Server Certificate” button. Select “Assign an Existing Certificate” and click Next. Select service’s certificate, click Next and finish the wizard.

Now virtual folder that hosts the service could be configured. Open its properties in the IIS Manager and select the “Directory Security” tab. First of all disable all authentication options except anonymous authentication in “Anonymous access and authentication control”.

Then click the Edit button on the “Secure communications” group. Check “Require secure channel”, select “Require client certificates”, check “Enable client certificate mapping” and click Edit button.

Now click the Add button, browse for the certificate file to be mapped and specify Windows account’s credentials.

Note: IIS should be restarted each time the security settings are changed!

The whole solution with sources and setup scripts could be downloaded from here (TransportSecurity.zip, 18.5 KB).

Advertisements

Written by Sergey Shishkin

21.06.2006 at 13:11

Posted in Uncategorized

%d bloggers like this: