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!

Coding Horror





I’ve been a fan of the iPhone since the 3G came out and it’s been the first phone that I’ve actually enjoyed using throughout its entire lifespan. Upgrading to the iPhone 4 was something of a massive upgrade at the time as well – the speed difference alone is outstanding when compared to the 2 year old 3G! However, the one thing that’s always bugged me was the fact I needed an MacBook to develop on it. I can’t afford one basically, so that means I can’t develop on their platform. Fair enough, it’s their call and I’m not overly worried about it for now. However, when Microsoft announced that they would be launching themselves into the mobile phone operating systems market (and Windows Mobile 6.5 doesn’t count as “being in the market – I’m sorry) and that I’d be able to write apps written in Silverlight and C# – I was sold. I had to get one of the devices. I whittled down the time to launch by drinking coffee and occasionally leaving the house. Eventually, I managed to get hold of a device and begin to use it. I thought I’d write my observations down for anyone interested to know how they both compare.