Friday, July 3, 2009

WCF Client Authentication using X509 certificates on SSL

In one of my project; there is a requirement

a. Web Services (WCF) Clients should be authenticated by X509 certificates.

b. Clients should validate the web services using X509 certificate (using SSL).

c. All these services should be built using basicHttpBinding and to be consumed by .Net 2.0 clients.

I am describing here the complete solution to achieve this and the settings to be done in IIS.

SSL Layer: In order to run web services on SSL; you need to get certificate from the certificate authority like VeriSign; however in development environment sometimes you donot have certificate from valid Authority and need to generate self signed certificate. Microsoft provides a utility to generate self signed certificates.

makecert.exe -r -pe -a sha1 -n CN=”Ent.com” -sr LocalMachine -ss My -sky exchange -b 01/01/2000 -e 01/01/2036 Ent.cer

The above command creates the self signed certificates and places this certificate in the Local Computer Account under personal store.

The configuration in IIS is simple as shown below:

a. Right Click WebSite (say Ent.com, where your WCF services are hosted). Go to properties and then go to Directory Security tab.


b. Click Server Certificate button. This will open the wizard to install certificate. Click “Next” and you will see the following screen. Select option “Assign an existing certificate” and click “Next”.

c. Now select the certificate you created “Ent.com” and click “Next”.


  1. Select the port number (for SSL default is 443) and click finish.
  2. Now if you want to enable 128 bit encryption you can edit the certificate details by clicking “Edit” button on the Directory Security tab and click the checkbox “Require 128-bit encryption”.


Some configuration changes need to be done in web.config file (WCF hosting configuration file). These configuration settings are described at the end of article.


That’ it you are done with installing SSL.

Now you can Add Web Reference in your .Net 2.0 project. However when you try to access the Web Service from your client application you will get the following error:


“The underlying connection was closed: Could not establish trust relationship for the
SSL/TLS secure channel.”


This exception is raised by client during SSL handshake because Server certificate is not issued b
y valid Authority.

In order to fix this problem, you need to tell client that it allows your certificate. The following code needs to be written and called before calling the service method.

protected void button1_Click(object sender, EventArgs e)

{

Testing.Service service1 = new Testing.Service();

ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(customCertificateValidation);

string data = service1.GetData(5, true);

label1.Text = data;

}

private bool customCertificateValidation(object sender, X509Certificate cert, X509Chain chain, System.Net.Security.SslPolicyErrors error)

{

//analyze the certificate and then return true.

return true;

}

The custom certificate validation method allows clients applications to decide which server certificates they can trust.

Note: Allowing self signed certificates is not recommended in Production environment.



X509 Client Certificate Authentication:

The next thing to do is client authentication using X509 certificates. In order to do this you need to change configuration in IIS and also in web.config file.

IIS settings

a. Right Click web site (say Ent.com). Go to properties and then go to Directory Security tab.


b. Click Edit button and then on “Client Certificate” Section select the option “Require client certificates”


Client Side Changes

Now when you call the web service you need to provide client Certificate. Here is the code:

protected void button1_Click(object sender, EventArgs e)

{

Testing.Service service1 = new Testing.Service();

//As an example loading certificate from file system.

X509Certificate cert = X509Certificate2.CreateFromCertFile(@"C:\Test1.cer");

service1.ClientCertificates.Add(cert); //adding cleint certificate.

ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(customXertificateValidation);

string data = service1.GetData(5, true);

label1.Text = data;

}

private bool customXertificateValidation(object sender, X509Certificate cert, X509Chain chain, System.Net.Security.SslPolicyErrors error)

{

//analyze the certificate and then return true.

return true;

}

The “Test1” certificate used here is self signed certificate. In production environment this certificate should be issued by valid authority. However to fix the problem in development environment you need to add the certificate in the “Trusted Root Certification Authorities” on you WCF hosting machine.


Web.config changes:

The complete config file is:

<system.serviceModel>

<services>

<service name="Service" behaviorConfiguration="ServiceBehavior">

<endpoint address="https://Ent.com/Services/Service.svc" binding="basicHttpBinding" contract="IService" bindingConfiguration="Binding">

<identity>

<dns value="localhost"/>

identity>

endpoint>

service>

services>

<behaviors>

<serviceBehaviors>

<behavior name="ServiceBehavior">

<serviceMetadata httpsGetEnabled="true"/>

<serviceDebug includeExceptionDetailInFaults="true"/>

behavior>

serviceBehaviors>

behaviors>

<bindings>

<basicHttpBinding>

<binding name="Binding">

<security mode="Transport">

<transport clientCredentialType="Certificate"/>

security>

binding>

basicHttpBinding>

bindings>

system.serviceModel>


Description:

  • In order to use SSL you need to tell that you are using transport layer security using the tag.

<security mode="Transport">security>

  • In order to use client certificates for authentication you need to specify ClientCertificateType as Certificate

    <security mode="Transport">
    <transport clientCredentialType="Certificate"/>

    <security/>

However after changes these settings when you try to run the application you will get strange error saying:

Client found response content type of '', but expected 'text/xml'.
The request failed with an empty response.

In order to fix this error you need to comment the following line in web.config file

< ! - - < address="mex" binding="mexHttpsBinding" contract="IMetadataExchange">- - >

That’s all. Now you can use WCF services from .Net 2.0 clients on SSL and using client certificate authentication.


The complete Code can be downloaded from here

You can generate the certificate using Microsoft tool as described above.


Ashwani Kumar

Solutions Architect

Globallogic Inc.

Thursday, April 9, 2009

Custom Code for SAML 1.1 digital verification

Attached is the custom code for Digital verification of SAML 1.1 Token

Source Code

Saturday, March 7, 2009

Custom Forms Authentication in ASP.Net and Classic ASP

Custom Implementation of Forms Authentication

Classic ASP developers often had to "roll their own" authentication scheme, however, in ASP.NET much of the grunt work has been taken out. In this blog I will be explaining the technology behind the forms authentication and you can write your own forms authentication in Classic ASP or in any other language. Recently I have implemented Forms Authentication in Classic ASP.

Security Overview

One of the new features of ASP.NET is Forms Authentication. Like in classic ASP, where custom database authentication occurred through the user entering his or her login credentials via an HTML form, ASP.NET Forms Authentication works similarly. However, using this neat feature, many of the mundane tasks and code that you were required to write in classic ASP are handled for you automagically.

When an unauthenticated user attempts to access a Forms Authentication protected web site the following process occurs:

1) The Forms Authentication HttpModule redirects the user to the logon page

2) User enter user name and password

3) A FormsAuthenticationTicket is constructed based on configuration information

4) The FormsAuthentication.Encrypt() method is called and returns a hex string containing the user’s ticket.

5) The encrypted ticket string is then placed into a cookie and added to the user’s cookie collection.

What is forms authentication ticket and forms authentication cookie? How are they related?

Forms authentication cookie is nothing but the container for forms authentication ticket. The ticket is passed as the value of the forms authentication cookie with each request and is used by forms authentication, on the server, to identify an authenticated user.

However, if we choose to use cookieless forms authentication, the ticket will be passed in the URL in an encrypted format. Cookieless forms authentication is used because sometimes the client browsers block cookies.

Forms Authentication Ticket

The forms authentication ticket is used to tell the ASP.NET application who you are. Thus, ticket is building block of Forms Authentication's security.


The ticket is encrypted and signed using the configuration element of the server's Machine.config file. ASP.NET 2.0 uses the decryptionKey and the new decryption attribute of the element to encrypt forms authentication tickets. The decryption attribute lets you specify the encryption algorithm to use. ASP.NET 1.1 and 1.0 use 3DES encryption, which is not configurable. Tampering with the ticket value is determined by a failure to decrypt the ticket on the server. As a result, the user will be redirected to the logon page.

If the application is deployed in a Web farm, you must make sure that the configuration files on each server share the same value for the validationKey and decryptionKey attributes in the tag, which are used for hashing and decryption of the ticket respectively. You must do this because you cannot guarantee which server will handle successive requests.

Generating Ticket in custom Forms Authentication Process

In the attached code FormsAuthentication.Encrypt() method of FormsAuthentication class is used to convert the ticket into a binary blob (byte array), digitally sign it, encrypt it, and format it in such a way that it can easily be place into a cookie.

In Microsoft’s implementation the conversion of the ticket into a binary blob is “black boxed” in a method named “CookieAuthContructTicket” (located in the native dll “webengine.dll”).

The ticket blob is constructed of the formatted property values defined in the FormsAuthencticationTicket class.

FormsAuthenticationTicket Properties:

Property Type Data

Name String Username

UserData String Any extra user information

CookiePath String The path the cookie is valid for ie. /AppPath

Expriation DateTime The time at which the ticket is invalid

IssueDate DateTime The time at which the ticket was issued

IsPersistent bool Indicates if the ticket is a persistent ticket

Version int The ticket version

These properties are formatted and combined into a binary blob.

1) Each string value is converted to a byte array (represented as a Unicode string).

2) Each DateTime value is converted into the equivalent “FileTime” (represented as a long value) and converted to a byte array.

3) IsPersistent and Version are cast to their equivalent byte representation

After these properties are converted to their byte array equivalents, the final ticket blob is constructed as described below.

1) Eight random bytes are generated and become the first 8 bytes of the final blob. This is done to ensure each encrypted ticket will differ even if the data is the same.

2) The “Version” byte is added at the 8th position of the blob.

3) Followed by the "Name" bytes

4) Followed by a 2 byte delimiter (both bytes are 0’s)

5) Followed by the "IssueDate" bytes

6) Followed by the "IsPersistent" byte

7) Followed by the “Expires” bytes

8) Followed by the “UserData” bytes

9) Another two byte delimiter is placed directly after the “UserData” bytes

10) Followed by the “CookiePath” bytes

11) Last but not least a final delimiter (another 0) is appended

Below is a detailed diagram of the final ticket blob….

Digitally sign the ticket:

After the ticket blob has been constructed it must be digitally signed to ensure the information is not tampered with. The digital signature is an HMAC (Hashed Message Authentication Code) hash of the ticket blob. Depending on the configuration either MD5 or SHA1 is used for the HMAC. In managed code these classes are implemented as HMACMD5 and HMACSHA1. The digital signature is a 20 byte hash of the data. This hash is appended to the end of ticket blob.

Encrypting the ticket and signature:

At this point in the process we have a binary blob (byte array) containing all of the ticket information as well as the signature of this data. Before transporting this information it must be encrypted. Depending on the configuration the information is either encrypted using AES (implemented as RijndaelManaged in managed code) or 3Des (implemented as TripleDesCryptoServiceProvider in managed code).

Formatting the encrypted ticket and signature:

Given that the encrypted ticket will need to be inserted into a cookie, the final step is to create a string from the data blob. There are a couple of choices on how you can go about doing this. Many people convert byte arrays to Base64 strings and transport them that way. Microsoft went another, equally valid route, and chose to encode the data as a hex string.

Stuffing the ticket into a Cookie:

With our hex string in hand we are set to complete the process. The final step is to simply place the hex string into a cookie and send it off to the user’s browser.

Writing HTTP Module

At this point the user has been given the keys to the website. Each time the user attempts to visit a site HttpModule intercepts the request, retrieves the cookie, grabs the ticket from the cookie, and does the reverse of whole process described above. If the decryption succeeds (which will only happen if the same key is used for decryption) and the signature is valid then the user is given access to the page or site.

Sliding Expiration

Let us take an example: If the logon page is accessed at 5:00 00:00:00 PM, it should expire at 5:10 00:00:00 PM if the timeout attribute is 10 and the slidingExpiration attribute is set to TRUE. Now, if any Web page is browsed again at 5:05 00:00:00 PM, the cookies and ticket time-out period will be reset to 5:15 00:00:00 PM.


If the Web page is accessed before half of the expiration time passes, the ticket expiration time will not be reset. For example, if any Web page is accessed again at 5:04 00:00:00 PM, the cookies and ticket timeout period will not be reset. It’s the responsibility of the HTTP Module to reissue the ticket when sliding expiration is set.

Source Code: Download

The code consist of

  • Library project that contains the classes to implement the forms authentication. The library project also contains the HTTP Module which intercepts the request and validate the token or reissue the token in case of sliding expiration.

  • Web Project which uses the custom build forms authentication.

Read other related topics

Forms Authentication in WebFarm

Generating decryption and validation key.