C#.NET & Classic ASP Password Hashing

C#.NET & Classic ASP Password Hashing

Published on: Thursday, February 10, 2011 9:02:00 AM)

One of the things I've recently been working on is a solution to allow both a .NET application and a legacy VBScript / Classic ASP to be able to validate a specific username / password combination, comparing two hashed passwords. In the process, I discovered (with the help of Google and StackOverflow) that accessing .NET objects from Classic ASP isn't really all that hard! But to the issue at hand: Password Hashing! Sorting this out from the .NET side was relatively easy. For the purpose of this post, here is roughly what I have at the moment.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace Amadiere.Com.Utilities
    public static class Cryptography
        /// <summary>
        /// Encrypts a password based on the passed in Encryption method (SHA512Managed is a good starting
        /// point if you don't know which to use).
        /// </summary>
        /// <remarks>
        /// The passwordSalt parameter is required to ensure that rainbow tables cannot be used to
        /// lookup all usernames if the salt is discovered. This salt is OK to store in the database,
        /// along with the hashedPassword generated by this function.
        /// </remarks>
        /// <param name="password">The password to be encrypted.</param>
        /// <param name="passwordSalt">The individual grain of salt for that password.</param>
        /// <param name="method">The method by which to encrypt.</param>
        /// <returns>An string representing the hashed password (88 characters long).</returns>
        public static string Hash(string password, string passwordSalt, HashMethods method)
            string siteWideSalt = "THIS IS A SITE WIDE SALT, BUT COULD BE A GUID";
            string encryptedPassword;
            switch (method)
                    encryptedPassword = HashSHA512Managed(siteWideSalt + password + passwordSalt);
            return encryptedPassword;
        /// <summary>
        /// One-way encrypts the password into oblivion. If the same password and salt are provided, the
        /// same end string will be churned out the other end of this sausage machine.
        /// </summary>
        /// <see cref="http://msdn.microsoft.com/en-us/library/system.security.cryptography.sha512managed.aspx" />

        /// <example>HashSHA512Managed("bobsYourUncle_SALT-GOES-HERE");</example>
        /// <param name="password">Unencoded pre-salted password.</param>
        /// <returns>An 88 character string, representing the originally encoded password.</returns>
        private static string HashSHA512Managed(string saltedPassword)
            UnicodeEncoding uniEncode = new UnicodeEncoding();
            SHA512Managed sha = new SHA512Managed();
            byte[] bytePassword = uniEncode.GetBytes(saltedPassword);
            byte[] hash = sha.ComputeHash(bytePassword);
            return Convert.ToBase64String(hash);
    public enum HashMethods

The Classic ASP side of things wasn't as easy. There are no built in libraries for SHA512 (or in fact, many other password hashing algorithms). So I had a few options on how I was to proceed:

The last option won - because it worked, and because it meant a lot less maintenance and praying for things to keep working. This is the code that pretty much does the same as the above .NET code, but in VBScript.

Function Hash(strPassword, strIndividualSalt)
  Hash = HashSHA512Managed(strSiteWideSalt & strPassword & strIndividualSalt)
End Function
Function HashSHA512Managed(saltedPassword)
  Dim objMD5, objUTF8
  Dim arrByte
  Dim strHash
  Set objUnicode = CreateObject("System.Text.UnicodeEncoding")
  Set objSHA512 = Server.CreateObject("System.Security.Cryptography.SHA512Managed")
  arrByte = objUnicode.GetBytes_4(saltedPassword)
  strHash = objSHA512.ComputeHash_2((arrByte))
  HashSHA512Managed = ToBase64String(strHash)
End Function
Function ToBase64String(rabyt)
  'Ref: http://stackoverflow.com/questions/1118947/converting-binary-file-to-base64-string
  Dim xml: Set xml = CreateObject("MSXML2.DOMDocument.3.0")
  xml.LoadXml ""
  xml.documentElement.dataType = "bin.base64"
  xml.documentElement.nodeTypedValue = rabyt
  ToBase64String = Replace(xml.documentElement.Text,VbLf, "")
End Function

As you can see in the VBScript example, I can create the .NET objects as I would any other type of object in VBScript, the difference comes in how I use them. Normally, in C#, you'd simply use ComputeHash() and there would be a number of overloads for you to choose from. As VBScript doesn't have the concept of overloading, you have to use a crazy-mad way of accessing the specific overload you want - using an underscore. I've not really come up with a full-proof way of working out which is which (though, if I'm honest, I didn't try much). I did however find out that they started ComputeHash(), ComputeHash_1() and ComputeHash_2() - I assume the numbers resemble the order they appear in Visual Studio when using intellisense for C# - but trial and error is normally good enough.