Santry Technology Solutions, Content Management, DotNetNuke, SharePoint Consulting
Register | Login
Friday, March 12, 2010

Sections
  
About Us
  
Partners
Downloads
  
 WWWCoder.com Resource Directory

Creating a Single Sign-on across ASP.NET Application and Legacy ASP Application
3/8/2004 10:01:07 AM

Encryption and hashing are used in this project to solve a common problem faced by many programmers, which is to create a single sign-on across multiple applications, which might be a mix of legacy ASP applications and ASP.NET applications.

The Scenario

For many membership websites, it is a very common scenario to limit access of some services to members only and require users to login. But oftentimes these different services are served under different applications or even under different platforms. This time we want to solve the problem of making a unified login that can be shared among legacy ASP application and ASP.NET application.


The First Thought

The first thought is always to try to dig out anything that can be utilized in the .NET framework. The build-in FormsAuthentication class under System.Web.Security namespace is the best candidate. By calling the SetAuthCookie(string, bool) method in FormsAuthentication, an authentication ticket for the given user identity can be created and attached to the cookie's collection of the outgoing response. Later by checking the Request.IsAuthenticated value, the user's login status can be verified. This works very well among pure ASP.NET applications. But when it comes to ASP applications, these methods or objects are obviously not available, or I should say, not easy to get implemented in the same way that ASP.NET does. Since we really don¡¯t know the detail inside FormsAuthentication or related classes, there is no easy way we can modify or mimic these classes to make them available to classic ASP application.

How about sharing these classes in GAC that can be called by ASP script? The FormsAuthentication class fails when called from ASP script because it simply needs a FORM to be initialized. If the FormsAuthentication and related classes can be modified, we can probably get rid of some initialization requirements that are not needed.



The Solution

We need to make our own authentication class.

First we create the EncryptString class to serve the Obfuscation of the user's identity as well as to verify the Obfuscated string. A triple DES encryption and a couple of MD5 hashes are involved.

using System;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Globalization;

namespace SingleLogin
{
   
   /// <summary>
   /// Provides a way to obfuscate the string.
   /// </summary>
   public class EncryptString
   {
      public EncryptString(){}

         public bool Verify(string pEncryptedString) 
         {
            return _Verify(pEncryptedString);
         }

         public string EncryptedString 
         {
            get 
            {
               return _Encrypt();
            }
         }

         public string UserName 
         {
            get 
            {
               return _UserName;
            }
            set
            {
               _UserName = value;
            }
        }

        public string Key 
        {
            get 
            {
               return _Key;
            }
            set
            {
               _Key = value;
            }
        }

        private string _Encrypt() 
        {
           MD5CryptoServiceProvider MD5 = new MD5CryptoServiceProvider();
           byte[] bKey = MD5.ComputeHash(Encoding.ASCII.GetBytes(_Key));
           byte[] bUserName = MD5.ComputeHash(Encoding.ASCII.GetBytes(_UserName));
           byte[] eUserName = MD5.ComputeHash(_Mix(bUserName, bKey));
           TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
           byte[] eKey = des.CreateEncryptor(_Key192, _IV192).TransformFinalBlock(bKey, 0, bKey.Length);
           return _ByteToHexString(eUserName) + _ByteToHexString(eKey);
        }

        private bool _Verify(string pEncryptedString) 
        {
            try 
            {
               byte[] eKey = _HexToByteArray(pEncryptedString.Substring(32));
               byte[] eUserName = _HexToByteArray(pEncryptedString.Substring(0, 32));
               MD5CryptoServiceProvider MD5 = new MD5CryptoServiceProvider();
               TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
               byte[] bKey = des.CreateDecryptor(_Key192, _IV192).TransformFinalBlock(eKey, 0, eKey.Length);
               byte[] bUserName = MD5.ComputeHash(Encoding.ASCII.GetBytes(_UserName));
               byte[] eUserName1 = MD5.ComputeHash(_Mix(bUserName, bKey));
               return (_ByteToHexString(eUserName) == _ByteToHexString(eUserName1));
            }
            catch (CryptographicException) 
            {
            }
            catch (FormatException) 
            {
            }
            return false;
        }

        private byte[] _Mix(byte[] pUserName, byte[] pKey)
        {
           byte[] retByteArray = new byte[pUserName.Length * 2];
           int i = 0, j;
           foreach(byte b in pUserName)
           {
              j = i * 2;
              retByteArray[j] = pUserName[i];
              retByteArray[j+1] = pKey[i];
              i++;
           }
           return retByteArray;
        }

        private string _ByteToHexString(byte[] pByteArray)
        {
           string retHexString = "";
           foreach(byte b in pByteArray)
           {
              retHexString += b.ToString("x2");
           }
           return retHexString;
        }

        private static byte[] _HexToByteArray(string pHexString)
        {
           int length = pHexString.Length/2;
           byte[] retByteArray = new byte[length];
           for(int i=0; i< length; i++)
           {
              retByteArray[i] = Byte.Parse(pHexString.Substring(i*2, 2), NumberStyles.HexNumber);
           }
           return retByteArray;
        }
		
        // The key used for the triple DES encryption
        private readonly byte[] _Key192 = new byte[24] {
           142, 216, 90, 16, 178, 40, 8, 32,
           35, 167, 34, 80, 226, 200, 125, 192,
           2, 94, 51, 204, 139, 35, 14, 19};
		
        // The Initialization Vector for the triple DES encryption
        private readonly byte[] _IV192 = new byte[24] {
           54, 173, 246, 179, 36, 99, 197, 3,
           42, 65, 62, 38, 134, 7, 29, 123, 145,
           23, 200, 58, 193, 10, 111, 232};

        private string _UserName, _Key;

   }
}

The process looks like the following:

The Authentication class then can be created:

using System;
using System.Web;
using System.Text;

namespace SingleLogin
{
	
    /// <summary>
    /// Summary description for Authenticate.
    /// </summary>
    public class Authentication
    {
       public Authentication()
       {
       }

       public bool IsAuthenticated(string pEncString, string pUserName)
       {
          EncryptString sqs = new EncryptString();
          sqs.UserName = pUserName;
          return sqs.Verify(pEncString);
       }

       public string GetAuthenticateCode(string pUserName)
       {
          EncryptString sqs = new EncryptString();
          sqs.UserName = pUserName;
          sqs.Key = _GenerateRandomKey(8);
          return sqs.EncryptedString;
       }

       private string _GenerateRandomKey(int pKeyLength)
       {
          Random random = new Random((int)DateTime.Now.Ticks);
          int charLength = chars.Length;
          StringBuilder sb = new StringBuilder();
          for (int i=0; i< pKeyLength; i++) 
          {
             int idx = random.Next(0, charLength);
             sb.Append(chars.Substring(idx, 1));
          }
          return sb.ToString();

       }

       private const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
   }
}

To test our solution in ASP application, we first register our assembly with GAC. a reference can be found at  http://msdn.microsoft.com/msdnmag/issues/01/08/interop/default.aspx with the part Using .NET Objects from COM.

The shortcut to register the assembly in GAC is to locate the SingleLogin.dll that can be built in our solution (see the attached solution package) and run the following two .NET commands:
regasm SingleLogin.dll
gacutil /i SingleLogin.dll

After that, you can run the following ASP script on the web server to test if the correct authenticated string can be generated based on the user's identity.

<%
   Set test = Server.CreateObject("SingleLogin.Authentication")
   ' get the authenticate string
   cookie = test.GetAuthenticateCode("demo")
   response.write cookie
   ' test the authenticate string with the original username
   name = test.IsAuthenticated(cookie, "demo")
   response.write "<br>" & name
   ' test the authenticate string with a wrong username
   name = test.IsAuthenticated(cookie, "ting")
   response.write "<br>" & name
   set test = nothing
%>

A similar ASP.NET web sample is also included in the attached solution package.

When the applications share the same authentication methods, a single sign-on can then be created among them.

Some Final Thoughts

There are some risks to authenticate users in the way we do. For example, the authentication cookie can be hijacked and the hijacker can reuse the cookie to authenticate himself. But this happens anyway even if you are authenticating users with the build-in FormsAuthentication class. Another vulnerability is the computed key can be compromised and used to generate the authentication ticket. In this case, the attacker has to compromise the triple DES encryption.

Still, there is plenty of space to think of for restructuring or customizing the process of encryption. Any comments are welcome here.

References

.NET Interop: Get Ready for Microsoft .NET by Using Wrappers to Interact with COM-based Applications
http://msdn.microsoft.com/msdnmag/issues/01/08/interop/default.aspx

Secure Query Strings: Preventing the Tampering of Data Passed between Applications
http://www.dotnetjunkies.com/how%20to/99201486-ACFD-4607-A0CC-99E75836DC72.dcik

Download the code!


About the Author:
Ting Huang, a senior programmer for a fast growing Minnesota based online marketing company and spends most of his time deep in system architect, ASP.NET and SQL Server.

 

Related Articles
   Related Document Creating a Single Sign-on for ASP.NET Application and Legacy ASP Application (Part II)


Page Options:
format for printing  Format for Printer
email article  Email Page
add to your favorites   Add to Favorites
How would you rate the quality of this content?
Poor - - Excellent
Comments?
Overall Rating:
Comments Left:
Left on 7/27/2008 4:32:12 AM by Anonymous
Comments:
Left on 11/11/2007 7:52:49 PM by Anonymous
Comments: Hello! Good Site! Thanks you! lcydzerjojrqq
Left on 7/6/2005 8:54:22 AM by Anonymous
Comments: Ting Huang, how do I get in touch with you???
Left on 6/29/2005 4:15:44 AM by Anonymous
Comments: continuation - at least we can easily check
that the information was changed ;-).

In your scenario, you only want to encrypt the username.
If someone alters the encrypted string,
if will be decrypted into an invalid username, so you don't need to add a hash (But it's good practice to add one).
So is your encryption function more secure than: Encrypt(Username, IV) = DES(Username, IV).
Does the random number bring more security ?
No ratings available.
Left on 6/29/2005 4:14:03 AM by Anonymous
Comments: Your encryption function uses a username, a random number and an initialization vector as parameters.
This information is then mixed altogether using MD5 Hash and DES encrytpion to form the
resulting encrypted string.
If I want to forge a encrypted string, the only thing that stops me
is the initializarion vector (as you mentionned).
We usually encrypt information so that it remain secret, and we append a hash so that it can't be changed (or at least we can easily check
that the information was changed ;-).

In your scenario, you only want to encrypt the username.
If someone alters the encrypted string,
if will be decrypted into an invalid username, so you don't need to add a hash (But it's good practice to add one).
So is your encryption function more secure than: Encrypt(Username, IV) = DES(Username, IV).
Does the random number bring more security ?
No ratings available.
Left on 6/27/2005 9:44:33 PM by Anonymous
Comments: Excellent Stuff
Left on 4/29/2005 10:59:15 AM by Anonymous
Comments: Comments from the following blog: Ting Huang, located at: http://blogs.wwwcoder.com/huangting/archive/0001/01/01/3884.aspx
No ratings available.
Left on 4/28/2005 9:05:46 PM by Anonymous
Comments: Comments from the following blog: Ting Huang, located at: http://blogs.wwwcoder.com/huangting/archive/2005/04/28/3884.aspx
No ratings available.
Left on 3/8/2005 8:46:22 AM by Anonymous
Comments: For a cross-platform Open Source Single Sign-On solution that supports ASP, check the JOSSO project at : http://www.josso.org

No ratings available.
Left on 10/19/2004 12:56:20 AM by Anonymous
Comments: i am unable to down load the code
No ratings available.
Left on 10/18/2004 5:41:35 AM by Anonymous
Comments: I can't download the code!
No ratings available.
Left on 5/18/2004 11:14:39 AM by Anonymous
Comments: This is a great article. I have been working on a similar problem, and this helps a lot.

However, what this solution lacks is the ability to unify logins for applications running on physically separate servers, perhaps even different domains. An example of such a situation would be a large organization with both Internet and intranet servers as well as multiple application servers and perhaps even a web farm.

Still, the article does provide a great solution for those who are not concerned with using multiple servers, and it deserves a 5.
Left on 3/23/2004 11:08:51 PM by Anonymous
Comments: Comments from the following blog: Scott's Blog, located at: http://blog.spebsqsa.org/scott/archive/2004/03/23/151.aspx
No ratings available.
Left on 3/22/2004 1:34:18 PM by Anonymous
Comments: Comments from the following blog: -:[web caboodle]:-, located at: http://blog.dannyboyd.com/archive/2004/03/22/236.aspx
No ratings available.
Left on 3/18/2004 5:37:54 PM by Anonymous
Comments: Comments from the following blog: Ryan Martin, located at: http://lab.nexus6studio.com/blogs/rmartin/posts/249.aspx
No ratings available.
  

 Latest Articles
  

 Latest News
  

 

Spotlight
Syndication

 


 


Digg This
 


DotNetNuke Platinum Benefactor

  
 

 Terms Of Use | Privacy Statement
 Copyright 2008 - Santry Technology Solutions, Box 172, Girard, PA 16417, (814) 774-0970