Amadiere.com

Fourteen and a half crazy frog burpers

10th February 2011

C#.NET & Classic ASP Password Hashing

Filed under: ASP.NET,C#,Classic ASP — Tags: , , , , — Alex Holt @ 9:02 pm

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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)
            {
                default:
                    encryptedPassword = HashSHA512Managed(siteWideSalt + password + passwordSalt);
                    break;
            }
            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
    {
        SHA512
    }
}

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:

  • Abandon my choice of SHA512 and go with MD5 where there seemed to be a bit more usage in the community. I was reluctant to do this because it isn’t as good as SHA512.
  • Copy some code sample that has been created by someone else, that I either have to accept is OK, or spend a good deal of time understanding it and breaking it down bit by bit.
  • Create a custom COM object in .NET, register it in the GAC and reference that via Classic ASP.
  • Access the .NET functions directly from Classic ASP.

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Function Hash(strPassword, strIndividualSalt)
 
  Const strSiteWideSalt = "THIS IS A SITE WIDE SALT, BUT COULD BE A GUID"
  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.

Hope this is of use to someone else! If anything, I’m sure I’ll find a need to do this again someday and I’m sure it’ll be useful for then! All this hashing has made me hungry!

Hashing passwords is hard! Let's eat!

Hashing passwords is hard! Let's eat!

Leave a comment

*

Theme designed & built for Amadiere.com by Alex Holt. Powered by WordPress