April, 2007

April, 2007

  • Care, Share and Grow!

    A quick walkthrough with myself...

    • 0 Comments

    Hi there,

    Well I am four blogs old now and am yet to introduce myself. Perhaps I had to muster enough strengths to let myself out here.

    To start with, My name is Saurabh Singh. I am a Computer Engineer; completed my B.E. in 2002. I am currently working with Microsoft GTSC, India. My job involves helping our customers and partners (that includes Web Developers and Administrators) to resolve their painstakingly complex issues related to IIS, ASP and ASP.NET.

    My interests range from diverse topics like Philosophy and Psychology to hardcore technical areas.

    I read a lot, but not technical stuffs most of the time :-).

    I am an avid fan of authors like Ayn Rand, Jeffrey Archer, Leo Tolstoy, Anton Chekhov and Ernest Hemingway. Movies which I would love to watch umpteen number of times include Forrest Gump, Pursuit of Happyness, Saving Private Ryan, Enemy at the gates, Gladiator and Notebook.

    My off-job hours go mostly in outdoor activities like swimming, Gym, and of course reading under a tree in solitude.

  • Care, Share and Grow!

    Accessing Request.CurrentExecutionFilePath property in Response.Redirect Vs Server.Transfer methods

    • 1 Comments

    Ok, I understand you might feel lethargic while viewing this blog of mine thinking, here again the same old crap about server.transfer and response.redirect.

    Don't know how many links you would already have found if you did a Live or Google on the net on the above commands. I at least found two dozens of them.

    So here I am not going to talk not about what they mean but a little weird scenario when it comes to using Request properties like Request.CurrentExecutionFilePath.

    We had a customer who had written his own custom HTTP Module which intercepts request and response for Asp.Net pages.

    Now, in his HTTP Module, he was trying to access the Request.CurrentExecutionFilePath property. Simple!

    The problem arises when you use the above mechanisms, especially using Server.Transfer.

    We know that in Response.Redirect the request flows between client and the server until we get the right path for the requested page.

    Client                                                                                                         Server

    GET /redirecttransfer/default.aspx HTTP/1.1                                        HTTP/1.1 302 Found

                                                                                               Location: /redirecttransfer/Default2.aspx

              GET /redirecttransfer/Default2.aspx HTTP/1.1                                      HTTP/1.1 302 Found

                                                                                                                   Location: /redirecttransfer/default3.aspx

    GET /redirecttransfer/default3.aspx HTTP/1.1                                      HTTP/1.1 200 OK

                                                                                                                   

    So here we have a complete round trip between the server and the client. The Request will have all the information about the requested page, accordingly we should get the right value in Request.CurrentExecutionFilePath (which should be Default3.aspx here). But what happens if you try to read the same property when using Server.Transfer instead of Response.Redirect?

     

    Let's say the request flow is: Default.aspx  >>--Server.Transfer--> Default2.aspx >>--Server.Transfer--> Default3.aspx

    Now in the HTTPModule under Application_EndRequest method, if we try to display the Request.CurrentExecutionFilePath property, what should we see?

    Guess....should it be Default.aspx, or Default2.aspx or Default3.aspx or something else altogether.

    Check it yourself, I am adding a sample HTTPModule here:

     

    public class HelloWorldModule : IHttpModule
    {
    public HelloWorldModule()
    {
    }

    public String ModuleName
    {
    get { return "HelloWorldModule" }
    }

    // In the Init function, register for HttpApplication
    // events by adding your handlers.
    public void Init(HttpApplication application)
    {
    application.BeginRequest += new EventHandler(this.Application_BeginRequest);
    application.EndRequest += new EventHandler(this.Application_EndRequest);
    }

    private void Application_BeginRequest(Object source, EventArgs e)
    {
    // Create HttpApplication and HttpContext objects to access
    // request and response properties.
    HttpApplication application = (HttpApplication)source;
    HttpContext context = application.Context;
    context.Response.Write("<h1><font color=red>HelloWorldModule: Beginning of Request</font></h1><hr>");
    }

    private void Application_EndRequest(Object source, EventArgs e)
    {
    HttpApplication application = (HttpApplication)source;
    HttpContext context = application.Context;
    context.Response.Write(context.Request.CurrentExecutionFilePath+"</br><hr><h1><font color=red>HelloWorldModule: End of Request</font></h1>");
    }

    public void Dispose()
    {
    }
    }

     

    Also let's say you have default.aspx, default2.aspx and default3.aspx as shown above in the flow diagram. We do a server.transfer from default.aspx to default2.aspx, which in turn does a server.transfer to default3.aspx page.

    Here are the codes:

    Default.aspx

    public partial class _Default : System.Web.UI.Page
    {
    protected void Page_Load(object sender, EventArgs e)
    {
    Response.Write(Request.CurrentExecutionFilePath);
    Server.Transfer("Default2.aspx");
    }
    }

     

    Default2.aspx

    public partial class Default2 : System.Web.UI.Page
    {
    protected void Page_Load(object sender, EventArgs e)
    {
    Response.Write(Request.CurrentExecutionFilePath);
    Server.Transfer("default3.aspx");
    }
    }

     

    Default3.aspx

    public partial class Default3 : System.Web.UI.Page
    {
    protected void Page_Load(object sender, EventArgs e)
    {
    Response.Write(Request.CurrentExecutionFilePath);

    }
    }

    You will notice that when you try to access the default.aspx page it does the server.redirect to default2.aspx which in turn again redirects to default3.aspx page and then finally the httpmodule displays the following:

     

    Now if you notice the HTTP Module displays /Default.aspx and not /default2.aspx or /default3.aspx.

    What can be the reason behind it. If you notice the Request.FilePath associated with Httpmodule is still /Default.aspx and not any of /default2.aspx or /default3.aspx page. The reason being that in addition to being faster than redirection, server.transfer preserves all of the ASP built-in objects from the original request, including form values from an HTTP post. The moment we have server.transfer in our code the currently executing request will stop and will forward the request to the new page.

    The new request also gets the original response stream further ahead for processing. Hence the property Request.CurrentExecutionFilePath will be same as the executing page when accessed in one of the above page class methods, but will be equal to the original request property if accessed in an HTTP Module. You can also consider it logically, why should there be a value for Request.CurrentExecutionFilePath in an HttpModule, it is called before or after the pages have been processed. So ideally it is not in context of any specific page. Hence it will show you the original page based on the assumption that the request was sent for the same page.

  • Care, Share and Grow!

    Getting little deeper: How ASP.Net Forms based authentication flows...

    • 6 Comments

    I have been recently supporting Asp.Net apart from IIS in the role of a Microsoft GTSC Developer Support Engineer.

    I had been a programmer earlier, but had more expertise on C, C++ and other unmanaged non-web stuffs. I am new to Web technology as per the programming background is concerned. I started working on specific topics which we regularly encounter in our daily support calls, and found Forms based authentication to be one of the most interesting and challenging topics to troubleshoot.

    Here i take a moment to dig deep in explaining the forms authentication. The below analysis helped me in a big way to understand how the HTTP traffice flows and what are the headers we need to concentrate upon.

    I will show a series of Web request/response flows when a user tries to access a website which is configured for Forms based authentication.

    Let's say, a user requests for accessing a protected web page on a site.

    So accordingly he should be redirected to a login page where he needs to enter the credentials and once validated against a user data store, he should be taken to the requested page.

    For our example I have written a very generic code as shown below:

     

    Default.aspx page, which checks whether user is authenticated or not. If yes, then it displays webpage content. If user is not authenticated he will be redirected to the login page. You can copy paste and try it for yourself.

    Default.aspx

    <%@Page Language="VB" %>
    <%@Import Namespace="System.Web.Security" %>

    <script language="vb" runat="server">

    Sub SignOut(objSender As Object, objArgs As EventArgs)
    'delete the users auth cookie and sign out
    FormsAuthentication.SignOut()
    'redirect the user to their referring page
    Response.Redirect(Request.UrlReferrer.ToString())
    End Sub

    Sub Page_Load()
    'verify authentication
    If User.Identity.IsAuthenticated Then
    'display Credential information
    displayCredentials.InnerHtml = "Current User : <b>" & User.Identity.Name & "</b>" & _
    "<br><br>Authentication Used : <b>" & User.Identity.AuthenticationType & "</b>"
    session("Name") = User.Identity.Name
    Else
    'Display Error Message
    displayCredentials.InnerHtml = "Sorry, you have not been authenticated."
    End If
    End Sub

    </script>

    <html>
    <head>
    <title>Forms Authentication</title>
    </head>
    <body bgcolor="#FFFFFF" text="#000000">
    <span class="Header">Forms Based Authentication using standard method</span>
    <br>
    <br>
    <div id="displayCredentials" runat="server" />
    <br>
    <br>
    <form runat="server" method="POST">
    <asp:TextBox id="TextBox1" runat="server" />
    <asp:Button id="cmdSignOut" text="Sign Out" runat="server" onClick="SignOut" />
    <asp:Button id="Button1" runat="server" text="Submit"/><br>
    <asp:TextBox id="TextBox2" runat="server" />
    </form>
    </body>
    </html>

    Login.aspx

    <%@Page Language="VB" %>
    <%@Import Namespace="System.Web.Security" %>

    <script language="VB" runat="server">

    Sub ProcessLogin(objSender As Object, objArgs As EventArgs)

    If FormsAuthentication.Authenticate(txtUser.Text, txtPassword.Text) Then
    FormsAuthentication.RedirectFromLoginPage(txtUser.Text, chkPersistLogin.Checked)
    Else
    ErrorMessage.InnerHtml = "<b>Something went wrong...</b> please re-enter your credentials..."
    End If

    End Sub

    </script>

    <html>
    <head>
    <title>Standard Forms Authentication Login Form</title>
    </head>

    <body bgcolor="#FFFFFF" text="#000000">
    <form runat="server">
    <table width="400" border="0" cellspacing="0" cellpadding="0">
    <tr>
    <td width="80">Username : </td>
    <td width="10"> </td>
    <td><asp:TextBox Id="txtUser" width="150" runat="server"/></td>
    </tr>
    <tr>
    <td>Password : </td>
    <td width="10"> </td>
    <td><asp:TextBox Id="txtPassword" width="150" TextMode="Password" runat="server"/></td>
    </tr>
    <tr>
    <tr>
    <td></td>
    <td width="10"> </td>
    <td><asp:CheckBox id="chkPersistLogin" runat="server" />Remember my credentials<br>
    </td>
    </tr>
    <tr>
    <td> </td>
    <td width="10"> </td>
    <td><asp:Button Id="cmdLogin" OnClick="ProcessLogin" Text="Login" runat="server" /></td>
    </tr>
    </table>
    <br>
    <br>
    <div id="ErrorMessage" runat="server" />
    </form>
    </body>
    </html>

    For demonstration purpose I have added users to the web.config files instead of using any other store like a SQL server or Active Directory store for storing user's credentials.

    Here is the web.config file section of our interest:

    <configuration>
    <system.web>
    <customErrors mode="Off"/>

    <authentication mode="Forms">
    <forms name="FormsAuthCookie" path="/" loginUrl="login.aspx" protection="All" timeout="1" slidingExpiration="false">
    <credentials passwordFormat="Clear">
    <user name="john" password="test1" />
    <user name="Randy" password="test2" />

    ....
    </credentials>
    </forms>
    </authentication>

    <authorization>
    <deny users="?" />
    </authorization>

    <sessionState
    mode="InProc"
    stateConnectionString="tcpip=127.0.0.1:42424"
    stateNetworkTimeout="10"
    sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI"
    sqlCommandTimeout="30"
    cookieless="UseCookies"
    cookieName="AppSessionCookie"
    timeout="2">
    </sessionState>

    </system.web>
    </configuration>

    Here if you notice, I have set the SlidingExpiration to False, which mean users will be logged out after a specific interval from the time they logged in, in our case it is set to timeout= "1" min.

    If you want you can encrypt the user's credentials using hash algorithm like SHA1 etc. but it is not in the agenda of this blog.

    I won't go much into the details about the settings here since you will get tonnes of articles on implementing forms based authentication on the net.

    I will basically show you how Request/Response flow occurs between the server and the client during Forms based authentication.

    Here we go:

    Step 1: Client sends a web request for the Default.aspx (or any page of your choice in the website except the login page; who would prefer to go through a login page to access one's desired webpage if given a chance :-)) page to the server.

    [You can focus only on the bold headers for our purpose]

    You type in the following url in the IE browser, http://saurabsi-sec/FormsAuthentication/default.aspx and hit Go!

    From Client

    GET /FormsAuthentication/default.aspx HTTP/1.1
    Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
    Accept-Language: en-us
    UA-CPU: x86
    Accept-Encoding: gzip, deflate
    User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; .NET CLR 1.1.4322; InfoPath.2)
    Host: saurabsi-sec
    Proxy-Connection: Keep-Alive

    Step 2: Server sends a response back to the Client with a status 302 Object Moved:

    From Server

    HTTP/1.1 302 Found
    Date: Thu, 19 Apr 2007 07:11:43 GMT
    Server: Microsoft-IIS/6.0
    X-Powered-By: ASP.NET
    MicrosoftOfficeWebServer: 5.0_Pub
    X-AspNet-Version: 2.0.50727
    Location: /FormsAuthentication/login.aspx?ReturnUrl=%2fFormsAuthentication%2fdefault.aspx
    Cache-Control: private
    Content-Type: text/html; charset=utf-8
    Content-Length: 196

    Notice the Response status code and the Location in the response header. Server says to the client that it (client) needs to resend a request to the url mentioned in the Location header.

    Step 3: Client then resends a new request for a /FormsAuthentication/login.aspx?ReturnUrl=%2fFormsAuthentication%2fdefault.aspx
    page. Now here since the client has to first get to the login.aspx page it will send a GET request first and not a POST request. Remember the first request to any site will be a GET and not POST.

    From Client

    GET /FormsAuthentication/login.aspx?ReturnUrl=%2fFormsAuthentication%2fdefault.aspx HTTP/1.1
    Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
    Accept-Language: en-us
    UA-CPU: x86
    Accept-Encoding: gzip, deflate
    User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; .NET CLR 1.1.4322; InfoPath.2)
    Host: saurabsi-sec
    Proxy-Connection: Keep-Alive

    Remember the querystring ReturnUrl shows the original requested page. Client is supposed to send a request later after authentication is done to this page. This is the way how the request/response maintains a track of the requested page throughout the transaction.

    Step 4: Server sends back the requested login.aspx page with a 200 OK Response.

    From Server

    HTTP/1.1 200 OK
    Date: Thu, 19 Apr 2007 07:11:43 GMT
    Server: Microsoft-IIS/6.0
    X-Powered-By: ASP.NET
    MicrosoftOfficeWebServer: 5.0_Pub
    X-AspNet-Version: 2.0.50727
    Set-Cookie: AppSessionCookie=vtd2qg55mkcypqnn53obxm45; path=/; HttpOnly
    Cache-Control: private
    Content-Type: text/html; charset=utf-8
    Content-Length: 1427

    Notice that Session ID gets created at this stage by the server and is sent along with the response to the client. From now onwards Server will keep track of a user's session using this cookie. Note the session ID gets created at this stage and not the authentication cookie. Remember Session key gets created the moment a successful transaction occurs like a 200 OK between the server and the client.

    Step 5: Now the client has recieved the login.aspx page. It enters the credentials for username and password and sends it across to the server again. Notice that this is a POST request to the login.aspx page now and this time it also sends credentials like username and password. We are sending the username and password as part of the Body of the request and not as part of the header.

    From Client

    POST /FormsAuthentication/login.aspx?ReturnUrl=%2fFormsAuthentication%2fdefault.aspx HTTP/1.1
    Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
    Referer:
    http://saurabsi-sec/FormsAuthentication/login.aspx?ReturnUrl=%2fFormsAuthentication%2fdefault.aspx
    Accept-Language: en-us
    Content-Type: application/x-www-form-urlencoded
    UA-CPU: x86
    Accept-Encoding: gzip, deflate
    User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; .NET CLR 1.1.4322; InfoPath.2)
    Proxy-Connection: Keep-Alive
    Content-Length: 277
    Host: saurabsi-sec
    Pragma: no-cache
    Cookie: AppSessionCookie=vtd2qg55mkcypqnn53obxm45

    You have the option of sending it as a querystring too, in such a case it will form a part of the request header and not body.

    If you check the Queystring here, it shows ReturnUrl=/FormsAuthentication/default.aspx

    Also checking the forms body, we find:

    txtUser=john
    txtPassword=test1
    cmdLogin=Login

    Step 6: Server receives the credentials and then goes ahead and authenticates the user. Once the user has been authenticated server responds back with a 302 Found response ,asking the client to send another request for the originally requested page, i.e. Default.aspx. How it determines the original requested page as default.aspx? You are right, it's through the querystring that we just discussed above.

    From Server

    HTTP/1.1 302 Found
    Date: Thu, 19 Apr 2007 07:11:50 GMT
    Server: Microsoft-IIS/6.0
    X-Powered-By: ASP.NET
    MicrosoftOfficeWebServer: 5.0_Pub
    X-AspNet-Version: 2.0.50727
    Location: /FormsAuthentication/default.aspx
    Set-Cookie:
    FormsAuthCookie=82A747623272A0A3A0C36EC6AD1FBA35A47592B1CFB38E54A1A7C5BC24ECAA2563715D4225EAE8927B98EC3DAD6FB9875E67FCA344AECCA19837A40B2311E373; path=/; HttpOnly
    Cache-Control: private
    Content-Type: text/html; charset=utf-8
    Content-Length: 1590

    Notice the FormsAuthCookie here, this has been set by the server once it authenticates the user. Server goes ahead and sends back this authentication cookie along with the response back to the client. Now next time the client sends back any request in the same session it should have the authentication cookie as well apart from the Session Cookie that was set earlier. Server will recognize the user and the ongoing session with the client based on the cookies sent to it in future requests.

    Step 7: Now is the final round wherein Client after done with all the validation process etc, goes ahead and sends request for the Default.aspx page (remember it was also the very first request sent by the client in the whole process).

    From client

    GET /FormsAuthentication/default.aspx HTTP/1.1
    Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
    Referer:
    http://saurabsi-sec/FormsAuthentication/login.aspx?ReturnUrl=%2fFormsAuthentication%2fdefault.aspx
    Accept-Language: en-us
    UA-CPU: x86
    Accept-Encoding: gzip, deflate
    User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; .NET CLR 1.1.4322; InfoPath.2)
    Proxy-Connection: Keep-Alive
    Host: saurabsi-sec
    Pragma: no-cache
    Cookie: AppSessionCookie=vtd2qg55mkcypqnn53obxm45; FormsAuthCookie=82A747623272A0A3A0C36EC6AD1FBA35A47592B1CFB38E54A1A7C5BC24ECAA2563715D4225EAE8927B98EC3DAD6FB9875E67FCA344AECCA19837A40B2311E373

    Notice the cookies here. You will see there are two cookies being sent to the server separated by ";". One for the server to recognize the ongoing session with the client and the other one to recongize the authenticated user.

    Step 8: Server has nothing else to do much but send back the response to the client for the requested web page.

    From Server

    HTTP/1.1 200 OK
    Date: Thu, 19 Apr 2007 07:11:50 GMT
    Server: Microsoft-IIS/6.0
    X-Powered-By: ASP.NET
    MicrosoftOfficeWebServer: 5.0_Pub
    X-AspNet-Version: 2.0.50727
    Cache-Control: private
    Content-Type: text/html; charset=utf-8
    Content-Length: 1111

    You might notice something here, in the web.config file we had set the timeout value for Forms authentication cookie to be 1 minute, and session cookie to be 2 minutes. So, you might see a scenario wherein let's say a user gets logged out because of expired authentication cookie. In our example let's say a user gets logged out after 1 minute (authentication cookie timeout value being set to 1 minute), and he is redirected to login page. He logs in back this time after entering the credentials in the login page. But this time he uses a different credentials to login.

    So what should happen, should the new user (with a different credentials this time) have access to all the original session variables (assuming session timeout has still not expired for the earlier user session) of the previous user when the same browser instance is running?

    Answer is Yes, the new user with a different credentials this time will have access to all the session variables for the previous user, provided the same browser session is being used this time. Resaon being that the authentication cookie has expired but not the session cookie, so browser sends the vaild session cookies to the server and hence is able to access the session variables.

    If we use a different browser session, of course a new session cookie has to be obtained which will invalidate session variables for the prevoius user.

    Here I haven't gone into troubleshooting session loss issues, rather i have focused on how forms authentication process occurs between client and the server. In case your eyes are looking for some good logical reading on troubleshooting Session loss issues in ASP.Net, a must READ here http://aspalliance.com/1182_Troubleshooting_Session_Related_Issues_in_ASPNET

  • Care, Share and Grow!

    How to setup IIS and AD for Client certificate authentication

    • 17 Comments

    Hi All,

    This post talks about how Client certificates are configured on websites. I have seen a lot of incidents where people get into issues with client certificate in particular, although server (website) certificates can give a scare at times.

    Here I will be walking you through the steps of configuring client certificates in your Windows 2003 environment (although there is not much of a difference in Windows 2000).

    Environment

    Windows 2003 (Web server) IIS6.0

    Windows 2000/XP/2003 (Client)

    Windows 2003 (Microsoft Certificate server)

    Walkthrough

    1. To enable SSL transaction between the server and the client, you need to have a server certificate installed on IIS website. Websites can get the server certificate from a trusted root Certificate Authority (CA). We will be focusing on the steps for acquiring client certificates and setting them in IIS for user authentication.

    2. Here I will show the screenshot of the steps that one needs to follow with brief explanation of the steps.

    Client Workstation: WIN2kIIS-VPC

    CA server: WIN2K3DC

    IIA Web Server: WIN2K3OWA

    DC: WIN2K3DC

    Domain: Anjenya.local

    Requesting a client certificate from a Trusted root Certificate Authority (CA):

    Access the CA Website from your client machine as http://Win2k3dc/certsrv

    image

     

    There are two ways of obtaining client certificate.

    Click on the link: Request a Certificate.

    image

     

    Click on “Select a certificate type: User certificate”.

    You can also obtain the certificate by clicking on “advanced certificate request” to add more specific details about the client certificate.

    image

    Click on More Options >>

    image

    Go ahead and hit Submit >

    image

    Click on “Yes”

    image

    Go ahead and click on the link to install the certificate. You might get the certificate directly as above or through email etc when in case of a 3rd party after verification.

    image

    Click on “Yes”

    image

    Now the User certificate is successfully installed on your client machine.

    You can check the certificate in two ways:

    1. Goto IE->Tools->Internet Options->Content->Certificates.

    You should see the certificate there under Personal store, which was installed on your client machine.

    image

    1. Or else you can open the Certificate snap-in through Start->Run->Mmc->Console->Add/Remove Snap-in->Add… -> Certificates.

    image

    Go ahead and add the certificates snap-in.

    image

    Double click on the certificate and you should see the details about it:

    image

    Enhanced Key usage will show you the purpose of this certificate.

    image

    The above picture shows that this certificate is meant for Client Authentication.

    image

    So here we finish the process of acquiring the client certificate.

    Now the next step is to map the client certificate in IIS manager, depending upon one’s requirements. It can be one of the following:

     

    • Option to accept the client certificate from the user by the IIS website (with no mapping enabled).
    • Option to have 1-to-1 mapping for client certificate.
    • Option to have Many-to-one mapping for client certificate.
    • Option to have Active Directory Mapping for client certificate.

    1-to-1 and Many-to-1 mapping are simple to setup.

    Here I will walk you through the process of setting up the above configuration for 1-to-1 mapping and Active directory mapping.

    Let’s say that you have a website in IIS for which you want to enable client certificate.

    You need to go to IIS Manager->Default Website-> right click and go to Properties->Directory Security->Under Secure Communications section, click on Edit.

    image

    image

    Here in the picture above, you have three options for Client Certificates:

    • Ignore client certificates: IIS will ignore client certificate when a request reaches IIS website, even though web request has the certificate in it.
    • Accept Client certificates: IIS website will accept any client certificate from the user, if it is along with the web request.
    • Require Client certificates: IIS website will check for client certificate along with web request. If no client certificate is in the web request, users shall see 403.7 – Client certificate required, as the error message in the web page response.

    Now in the next section in the same picture above, if you want your website to be configured such that a client certificate is mapped to a user account, you can check on “Enable client certificate mapping”. What it means is that request will be executed in the context of an account.

    image

    Now, when you enable 1-to -1 Mapping, an individual client certificate will be mapped to a specific Windows account. So in case you don’t want any of the IIS authentication methods to be used, like Anonymous, Basic, Digest or Windows Integrated authentication, you can rely upon client certificate authentication based on 1-to -1 or Many-to-1 mappings.

    We will first go ahead with 1-to-1 mapping:

    Click on “Add…” in the Account Mappings window shown above.

    Now before you map a client certificate with a windows account, you need to have the corresponding client certificate on the server.

    Export the client certificate from the CA or the client machine (where you have the certificate installed) as follows:

    1) From Client machine: Open Certificate snap-in as earlier and go to Certificates – Current User -> Personal -> Certificates.

    image

    Double click on the selected certificate and Click on Details and go to “Copy to File…”.

    Follow the Export wizard.

    image

    image

    You can either Export the private key or not export it. You should know the meaning of exporting the private key.

    image

    Go ahead and save the client certificate somewhere on your workstation (client).

    image

    2) From CA:

    Go to the Certificate Authority Snap-in and check the following location:

    image

    image

    Double click to display the certificate. Click on “Copy to File...” and follow the Certificate Export Wizard, and save the file to the server as shown below:

    image

    Now copy the saved certificate from any of the above location to the IIS server, where we need to map it a windows account.

    Back to IIS manager console for certificate mapping:

    image

    Now map a specific windows account with this certificate as shown below:

    image

    image

    Once the 1 to 1 mapping is set in place go ahead and try browsing the site.

    Here I have used an ASP script to render the server variables pertaining to the web request.

    This script will display the logged on user name and the authentication type used along with some other information.

    Also when you want to use Client certificate authentication you can clear all other authentication options in the IIS manager Directory Security setting as show below:

    image

    Here you won’t get 401.2 server configuration error because we are using some sort of authentication mechanism (client certificate mapping) to authenticate the user. Had we been not using client certificate mapping we would have got 401.2 if we try to access the site with no authentication method selected in IIS manager.

    Had there not been Client cert mapping and we had tried to browse to the web page with all the options cleared as shown above, you would have got error 401.2.

    Here is the sample logoninfo.asp page which displays server variables. Try accessing this page.

    <%

    response.write ("LOGON_USER: ")

    response.write (request.servervariables("LOGON_USER"))

    response.write ("<BR>")

    response.write ("AUTH_USER: ")

    response.write (request.servervariables("AUTH_USER"))

    response.write ("<BR>")

    response.write ("AUTH_TYPE: ")

    response.write (request.servervariables("AUTH_TYPE"))

    response.write ("<BR>")

    response.write ("CERT_COOKIE: ")

    response.write (request.servervariables("CERT_COOKIE"))

    response.write ("<BR>")

    response.write ("CERT_ISSUER: ")

    response.write (request.servervariables("CERT_ISSUER"))

    response.write ("<BR>")

    response.write ("CERT_KEYSIZE: ")

    response.write (request.servervariables("CERT_KEYSIZE"))

    response.write ("<BR>")

    response.write ("CERT_SERIALNUMBER: ")

    response.write (request.servervariables("CERT_SERIALNUMBER"))

    response.write ("<BR>")

    response.write ("CERT_SERVER_ISSUER: ")

    response.write (request.servervariables("CERT_SERVER_ISSUER"))

    response.write ("<BR>")

    response.write ("CERT_SERVER_SUBJECT: ")

    response.write (request.servervariables("CERT_SERVER_SUBJECT"))

    response.write ("<BR>")

    response.write ("CERT_SUBJECT: ")

    response.write (request.servervariables("CERT_SUBJECT"))

    %>

    Now in our example we try accessing the above script and we get the following response:

    image

    In the above step, if you disable Client cert and enable windows integrated authentication only, you should see something similar to the one shown below:

    Check the Authentication type.

    image

    Similarly you can try Many-to-1 mapping, please read MSDN/KB articles that talk about how to set it up…it’s very similar to 1 to 1 mapping.

    I would like to discuss Active Directory Mapping in particular here:

    We need to have Client certificate enabled, we can remove 1-to-1 and many-to-1 mapping from IIS Manager since we need to enable AD mapping.

    In AD mapping we need to follow the following steps:

    Go to the IIS Manager, right click on root level WEBSITES->Properties->Directory Security.

    Select “Enable the windows directory service mapper”.

    image

    Now go to Active directory, open Active directory users and computers, go to Users, and then select the user for which you want to map the certificate.

    Right click on the user name, go to Name Mappings. Add the client certificate. Now we have a mapping for that certificate to a user account in the AD.

    Go to the client machine and logon with the user credentials , and then try accessing the site now, and now you should be able to access the page and you should see the Logon name in the webpage, here the logon name will correspond to the same user with which we have associated the client certificate in the AD.

    Now you should see something like this:

    image

    Remember this:

    Here is an excerpt from a TechNet article:

    In Active Directory mapping, when the IIS server receives a certificate from the user, it passes it on to Active Directory, which maps it to a Windows 2000/2003 user account. The IIS server then logs this account on.

    Active directory mapping is most useful when the account mappings are the same on all IIS servers. Administration is simplified because the mapping is done in only one place.

    Mapping in Active Directory can happen in one of two ways. The administrator can explicitly map a certificate to a user's account. This certificate can come from any source--as long as the root CA for that certificate is trusted for client authentication.

    UPN mapping can also be used. A UPN is automatically put into a certificate issued by an enterprise CA. If a certificate is passed to Active Directory for mapping, it is first examined for UPN mapping. If UPN mapping is not possible, the mapping set by the administrator is used.

    UPNs are in the form of userid@domain. If the certificate contains a UPN, the domain is within the hierarchy of the directory, and the CA that issued the certificate is trusted to put UPNs in the certificate, then the user's account is retrieved from the directory and logged on. All these conditions must be true before the user's account is retrieved. If any of these conditions is false, the directory is searched for a mapping set by the administrator.

    In Active Directory mapping, when the IIS server receives a certificate from the user, it passes it on to Active Directory, which maps it to a Windows 2000 or Windows Server 2003 user account. The IIS server then logs on the account.

    You can create an Active Directory mapping in one of two ways. You can rely on UPN mapping, or, if UPN mapping is not possible, you can manually map a certificate to the account of a user.

    Use Active Directory mapping when the account mappings are identical on all IIS servers. Active Directory mapping is easier to maintain than IIS mapping because you only have to create the mapping in one location.

    NOTE: Let’s assume that the user account with which we are trying to access the site doesn’t have a UPN name in the AD (this might happen in the case where the logged on user is a local user and not a Domain user) then in that case the logon credentials for the request will be the mapped user account for the certificate in the AD. Else, if the client certificate’s “Issued to” is a domain user account, then logon credentials will use that Account and not the mapped account associated with certificate. Also it will not respect user’s logged on credentials or server authentication method in IIS manager.

Page 1 of 1 (4 items)