April, 2009

April, 2009

  • Care, Share and Grow!

    Automate client certificate one-to-one mapping in IIS 6.0 using C#

    • 32 Comments

    In PSS, we occasionally get requests from our customers wherein they want to automatically add entries for client certificate mapping in IIS or Active Directory (AD). That is either a 1-to-1, Many-to-1 or AD mapping for the client certificate authentication for the web site. I recommend going with AD mapping because that eases the management but it finally depends upon one's need.

    I am not sure but I feel there is a security breach plus annoyance when an administrator has to laboriously enter the mappings for all the accounts/certificates (I am being specific to 1-to-1/Many-to-1 here).

    The concern I feel when dealing with the administrator doing it for 1-to-1 and Many-to-1 are:

    a. If there are hundreds of users you need to do this manually for everyone of those accounts and it's a pain.

    b. Yes, the above can be automated using a script but then the second concern that arises is that whoever is running the script has to know the passwords for all these accounts to be mapped. I think this doesn't sound good.

    I have written a sample application using which users can enter the mappings themselves in the IIS's Client certificate setting, i.e. entries having the client certificate mapped to a windows account (either a local IIS or AD account) and the corresponding password.

    So this is how it works:

    • User accesses this web page from their workstation which already has the client certificate installed.
    • They are authenticated over Basic with SSL.
    • Browser sends across the client certificate as part of the HTTP web request.
    • This application gathers the user account, password plus the client certificate from the incoming HTTP web request and does the mapping in IIS.

     

    image

    I am adding the code here in case someone may want to extract the section for automated scripting instead of using it as a web app.

    This code is also attached to this post as well.

    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Security.Cryptography.X509Certificates;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.DirectoryServices;
     
    /* This sample application is to automate One-to-One Client certificate mapping in IIS 6.0.
     * User should be able to access this site from the browser and select the client certificate
     * in their machine which will be mapped to their account on the IIS server for 1-to-1 mapping. 
     * You need to deploy this application on the IIS server which is hosting the website(s) which 
     * needs client certificate mapping, preferably under its own virtual directory.
     *
     * Important:
     * - Have the authentication for this web application configured to use Basic along with SSL.
     * - Have the "Accept client certificates" or "Require client certificates" selected under 
     *   <Website> -> Properties -> Directory Security -> Secure communications -> Edit -> Client certificates
     * - Ensure the website that we want the mapping for is mentioned in the web.config file associated with
     *   this application under <appSettings>
     * - In the Web.config file we are impersonating an Administrator account for this application. 
     *   <identity impersonate="true" userName="Administrator" password="myadminpassword"/>
     *   This is done because non-admin users cannot modify the IIS metabase. If you do not want users to map
     *   entries themselves through web page you can change this to <identity impersonate="true" />.
     *   In such a case only admins can add the mappings for their user accounts. Non-admins won't be able to 
     *   add the client mapping entries.
     *   This is valid for both domain or local Windows NT accounts.
     * - This app is written using .Net 2.0, ASP.Net 2.0 and above in mind. You should be able to make it work
     *   with ASP.Net 1.1 as well.
     * - You may prefer to run this application under its own dedicated application pool to ensure stability and security.
     * 
     * DISCLAIMER: The code is not tested for production scenarios so use it at your own risk. 
     *             In case one wants to use batch scripting etc or some kind of console app instead 
     *             of web app you can extract the code section from this page which should work fine for the job.
     */
     
    public partial class _Default : System.Web.UI.Page 
    {
        protected void Page_Load(object sender, EventArgs e)
          {
            Response.Write("<B>Client Certificate One-to-One Mapping Application:</B><BR><HR>");
            Response.Write("Serial number: " + Request.ClientCertificate.SerialNumber + "</BR><HR>");
            Response.Write("Issuer: " + Request.ClientCertificate.Issuer + "</BR><HR>");
            Response.Write("Subject Name: " + Request.ClientCertificate.Subject + "</BR><HR>");
            if (Request.ClientCertificate.IsPresent)
            {
                Response.Write("Validity<BR>");
                Response.Write("&nbsp;&nbsp;&nbsp;&nbsp;Not before: " + Request.ClientCertificate.ValidFrom + "</BR>");
                Response.Write("&nbsp;&nbsp;&nbsp;&nbsp;Not after: " + Request.ClientCertificate.ValidUntil + "</BR><HR>");
            }
            else
                Response.Write("<B>There is no client certificate sent along with the request!</B><HR>");
     
            Response.Write("Authenticated User: " + Request.ServerVariables["AUTH_USER"] + "</BR><HR>");
            Response.Write("Authentication Type: " + Request.ServerVariables["AUTH_TYPE"] + "</BR><HR>");
        }
        protected void Button1_Click(object sender, EventArgs e)
        {
            string user = Request.ServerVariables["AUTH_USER"];
            string password = Request.ServerVariables["AUTH_PASSWORD"];
            string clientCertMappingName = "Mapping for " + user;  // <--- Our One-to-One Mapping name for the entry
            HttpClientCertificate cert = Request.ClientCertificate;
            /*
              If you want to map a client certificate located on the disk instead of the one as part of the 
              HTTP Web request try the code below.
              
              X509Certificate certificate = X509Certificate2.CreateFromCertFile(@"c:\cert.cer");
              X509Certificate certificate = cert.Certificate;
              byte[] certHash = certificate.GetRawCertData();
            */
            byte[] certHash = Request.ClientCertificate.Certificate;
            try
            {
            //Get the name of the Web site for which mapping has to be done from the App settings in the web.config file.
            string friendlyWebsiteName = ConfigurationManager.AppSettings["websitename"].ToString();
     
            //Get the Site Identifier based on the friendly name of the Web Site.
            string siteId = getsiteid(friendlyWebsiteName);
     
            if (siteId != null)
                {
                string sitePath = "IIS://localhost/W3SVC/" + siteId + "/IIsCertMapper";
                using (DirectoryEntry de = new DirectoryEntry(sitePath))
                {
                    de.Invoke("CreateMapping", new object[] { certHash, user, password, clientCertMappingName, true });
                }
                Response.Write("Account Mapped: <B>" + Request.ServerVariables["AUTH_USER"] + "</B></BR>");
                Response.Write("Mapping Name: <B>" + "Mapping for " + Request.ServerVariables["AUTH_USER"] + "</B></BR>");
                Response.Write("Web Site: <B>" + friendlyWebsiteName + "</B></BR>");
                }
            else
                {
                Response.Write("<B>Web Site does not have a valid Site ID. Ensure we have the correct site name in the config file for this app.</B>");
                }
            }
            
            catch (System.Runtime.InteropServices.COMException)
            {
                Response.Write("A COM exception occurred while setting up the mapping.");
            }
            catch (SystemException)
            {
                Response.Write("An error occurred while setting up the mapping.");
            }
            catch (Exception)
            {
                Response.Write("An error occurred while setting up the mapping.");
            }
           
        }
     
        public string getsiteid(string websitename)
        {
            DirectoryEntry root = new DirectoryEntry("IIS://localhost/W3SVC");
            try
            {
                string siteid = null;
                foreach (DirectoryEntry de in root.Children)
                {
                    if (de.SchemaClassName == "IIsWebServer")
                    {
                        if (websitename.ToUpper() == de.Properties["ServerComment"].Value.ToString().ToUpper())
                            siteid = de.Name;
                    }
                }
                if (siteid == null) return null;
                return siteid;
            }
            catch
            {
                return null;
            }
            finally
            {
                root.Close();
            }
        }
    }

     

    Ciao

    Nice weekend!

  • Care, Share and Grow!

    Visual Studio 2005/2008 IDE: Web Controls in the Toolbox seen grayed out for Asp.Net projects ?

    • 4 Comments

    Here is a quick post that may help you save few hours if you get into this problem.

    Symptoms

    -- Out of blue you realize that when you try to create and open a new Web  site/Web Application project we do not see any of the Web controls in the Toolbox. It includes most of the sections in the Toolbox pane including Common controls, Data, Login, validation and in fact any of the controls which are associated with an Asp.Net web page.

    -- When you click on 'Show All' in the toolbox you see all the Asp.Net related controls grayed out. In fact you will see most if not all, of the sections in the toolbox being grayed out except HTML controls.

    image

     

    Go to the Source view for the Web form in the VS IDE and add an ASP control yourself like <asp:TextBox...></asp:TextBox>

    Browsing to the web page shows the control getting rendered without any issues. So seems like more of a VS IDE issue than anything to do with ASP.Net application configuration.

    -- Creating a new Winform project works fine with all the relevant controls being displayed and working in the Toolbox window.

     

    Resolution

    This issue may happen if by mistake you had associated ASPX pages with the HTML Editor in the VS IDE. It should be ideally associated with the Web form Editor.

    To resolve this, In the Solution Explorer, locate the ASPX that we are working on, right click on the page and select 'Open with...'.

    You may be seeing that the current selection is HTML Editor (Default). Change it Web Form Editor and click on 'Set as Default'.

    image

    Close the current VS IDE and re-launch it and now we should *hopefully* be good.

  • Care, Share and Grow!

    Unable to load DLL 'bcrypt.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

    • 2 Comments

    I recently had the privilege to get access to a machine from a colleague of mine. It was a Windows server 2003 server and I had to test some ASP.Net application for one of my pet projects. I was focusing completely on the project at hand before I was completely taken off by a surprise, although not a pleasant one.

    I found that my application was throwing the following exception, in fact forget my own application even a test Asp.Net 2.0 page having just a one word was failing. Also this happened for web resources hosted directly in IIS. If you run this app from within Cassini (ASP.Net Web server) you may not see this issue at all. This happened for both Website as well as WAP based applications hosted in IIS.

    Server Error in '/' Application.
    --------------------------------------------------------------------------------
    
    Unable to load DLL 'bcrypt.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E) 
    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 
    
    Exception Details: System.DllNotFoundException: Unable to load DLL 'bcrypt.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
    
    Source Error: 
    
    An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.  
    
    Stack Trace: 
    
    
    [DllNotFoundException: Unable to load DLL 'bcrypt.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)]
       Microsoft.Win32.Win32Native.BCryptGetFipsAlgorithmMode(Boolean& pfEnabled) +0
       System.Security.Cryptography.Utils.get_FipsAlgorithmPolicy() +140
       System.Security.Cryptography.RijndaelManaged..ctor() +13
       System.Web.Configuration.MachineKeySection.ConfigureEncryptionObject() +232
       System.Web.Configuration.MachineKeySection.EnsureConfig() +156
       System.Web.Configuration.MachineKeySection.GetEncodedData(Byte[] buf, Byte[] modifier, Int32 start, Int32& length) +37
       System.Web.UI.ObjectStateFormatter.Serialize(Object stateGraph) +166
       System.Web.UI.ObjectStateFormatter.System.Web.UI.IStateFormatter.Serialize(Object state) +4
       System.Web.UI.Util.SerializeWithAssert(IStateFormatter formatter, Object stateGraph) +37
       System.Web.UI.HiddenFieldPageStatePersister.Save() +79
       System.Web.UI.Page.SavePageStateToPersistenceMedium(Object state) +105
       System.Web.UI.Page.SaveAllState() +236
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1099
    
     
    
    
    --------------------------------------------------------------------------------
    Version Information: Microsoft .NET Framework Version:2.0.50727.3053; ASP.NET Version:2.0.50727.3053 

    This was quite perplexing as I couldn't find much information on this across the net. One incident I found talked about un-installation of  MS07-040 security update. I was running on .Net framework 2.0 Sp2, Windows Server 2003 SP2. No luck with it. I had no clues about this dll which was missing as in the exception and why the heck it was looking for it in the first place.

    The interesting part here was that the call stack looked like having some encryption/decryption algorithm (RijndaelManaged) being used perhaps related to viewstate. I finally had to disable the attribute EnableViewStateMac="false" for the web page to make it work, but well that may not be an option all the time for everyone.

    If you face such a scenario just don't go ahead with reinstallation of .Net framework 2.0, it may not help you but only drain your precious time.

     

    Analysis/Resolution

    From this KB article this is what RijndaelManaged is all about.

    "ASP.NET 2.0 uses the RijndaelManaged implementation of the AES algorithm when it processes view state data. The ReindaelManaged implementation has not been certified by the National Institute of Standards and Technology (NIST) as compliant with the Federal Information Processing Standard (FIPS). Therefore, the AES algorithm is not part of the Windows Platform FIPS validated cryptographic algorithms."

    To work around this either set EnableViewStateMac to false or else add the following entry as mentioned in the kb under <system.web> section for the web application.

    <machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey="AutoGenerate,IsolateApps" validation="3DES" decryption="3DES"/>

    ASP.NET use the Triple Data Encryption Standard (3DES) algorithm to process view state data instead of the AES (Rijndael) algorithm. Remember this is comparatively weaker than Rijndael based encryption and hence your application will be comparatively insecure.

     

    *Note that the error message in the above article is not exactly the same as what I saw here for this post but the resolution remains the same :-).

     

    Till next time..Beer mug

Page 1 of 1 (3 items)