Thursday, 29 November 2007

Commerce Server 2007 and password hashing

Commerce Server 2007 supports hashing and encryption of database fields, such as passwords, through encryption and/or hashing algorithms that can be specified in config. Out of the box CS2007 supports three hashing algorithms, SHA1, SHA256, and MD5. I think SHA1 is the default algorithm used for one-way hashing.

If you use the UpmMembershipProvider hashing and validation is handled for you. Should you need to hash or verify a password without the help of this provider, you may be in for a fun time trying to figure out how it all works.

Microsoft has published examples of how to validate CS2007 passwords and you can deduce from the examples how the whole hashing business takes place. However, there are two curious things to note about these examples - and they're not good:

  1. The examples ignore SHA1 hashing even though that's probably what most standard setups use.
  2. The GenerateSaltValue() method is just wrong. Try running the example and passing in null as a salt value to HashPassword() and you'll see what I mean.

Before I continue, a quick overview of the standard hashing procedure is appropriate.

Given a plain-text password, we generate a random salt value that that the hash algorithm uses to hash the plain-text password. Next we turn the salt value and the plain-text passwords into byte arrays, and then combine the two byte arrays into one with the salt preceding the plain-text password. Finally we call ComputeHash(myByteArray) on our specified hash algorithm instance. This call returns another byte array which we can then traverse to generate a string. The final hashed password consists of the salt value pre-pended on the hashed password. Look at the code examples below and this will make more sense. Note also that CS2007 uses a hexadecimal representation of the bytes in our hashed password, meaning that each byte is converted to a 2-digit hexadecimal string (myByte.ToString("x2")).

Now, back to the GenerateSaltValue() method in Microsoft's example. This method creates a byte array, fills it with random integers, and turns it into a string by calling GetString() on an instance of UnicodeEncoding. In the example, the size of the byte array is 4 (specified by the SaltValueSize member), which results a 2 character string. A quick look at the HashPassword() methods shows you that any salt value passed in as a string is expected to be 8 characters. Furthermore, the byte.Parse() calls fail because the input data is of an invalid format.

What the Microsoft example has left out is that the returnes salt string should be hexadecimal. So, once the byte array has been created, each byte should be turned into a two digit hex value and appended to a string. If you do that it will work.

It took me a while to figure this out, but one particular post on Christiano's blog shed some light on the problems I was having and helped me create a better solution, shown below.

If you want to use Microsoft's example as it stands, replace the GenerateSaltValue with this method:


public string GenerateSaltValue()
{
StringBuilder builder = new StringBuilder();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] salt = new byte[SaltValueSize];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(salt);

foreach (byte outputByte in salt)
builder.Append(outputByte.ToString("x2").ToUpper());

return builder.ToString();
}


Otherwise, the below class may be a starting point for your hashing:


using System;
using System.Collections.Generic;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;

public class Hasher
{
public readonly int SaltValueSize = 4;

public string Hash(string stringToHash, HashAlgorithm hash)
{
return Hash(stringToHash, null, hash);
}

public string Hash(string stringToHash, string saltValue, HashAlgorithm hash)
{
return ComputeHash(stringToHash, saltValue, hash);
}


private string ComputeHash(string stringToHash, string saltValue, HashAlgorithm hash)
{
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] hashedBytes;
byte[] salt = null;
byte[] dataBuffer;
byte[] stringToHashBytes;
StringBuilder builder = new StringBuilder();

if (saltValue == null)
salt = GetSalt();
else
salt = GetSaltFromString(saltValue);

dataBuffer = new byte[encoding.GetByteCount(stringToHash) + SaltValueSize];
stringToHashBytes = encoding.GetBytes(stringToHash);

salt.CopyTo(dataBuffer, 0);
stringToHashBytes.CopyTo(dataBuffer, SaltValueSize);

hashedBytes = hash.ComputeHash(dataBuffer);

foreach (byte outputByte in salt) builder.Append(outputByte.ToString("x2").ToUpper());
foreach (byte outputByte in hashedBytes) builder.Append(outputByte.ToString("x2").ToUpper());

return builder.ToString();
}

private byte[] GetSalt()
{
byte[] saltBytes = new byte[SaltValueSize];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(saltBytes);

return saltBytes;
}

private byte[] GetSaltFromString(string saltValue)
{
byte[] saltBytes = new byte[SaltValueSize];

binarySaltValue[0] = byte.Parse(saltValue.Substring(0, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
binarySaltValue[1] = byte.Parse(saltValue.Substring(2, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
binarySaltValue[2] = byte.Parse(saltValue.Substring(4, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
binarySaltValue[3] = byte.Parse(saltValue.Substring(6, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);

return saltBytes;
}

}

Wednesday, 28 November 2007

iPod challenge

What's on your iPod? How many songs, albums, and artists reside inside its shiny little chassis? If you were to ask me I would only be able to answer the question in part. The fact is, I've got way more music on my iPod than I have ever listened to. So the time has come to find out what's on my iPod.

I purchased my trusty 60GB iPod in 2006 just before moving from Australia to the UK so that I could carry my whole CD collection with me. Since then I've added a whole bunch of new CDs as well, and the result is 9087 songs, or 27.5 days of music, all stored in my pocket. But I think there are entire albums I've never listened to, or artists whose songs remain unplayed but for one or two.

So, starting tomorrow I will be playing all my iPod songs in order from 1 to 9087. How long will it take me? Well, let's see. 27.5 days of music equates to 660 hours. I can, maybe, listen to four hours of music a day. That makes for 165 days of listening. Since I mostly listen to my iPod at work that's 33 weeks. Zoiks!

Maybe by July next year I'll be done. Wow - somehow I think I might not make it through this one, but I think it would be fun to at least try because there's bound to be a few gems and a few weird hidden songs on there. So, tomorrow I start. And I'll blog about it as I go.

The blog is back

Using a custom domain name with Blogger is not as straight forward as it seems. Basically my blog went missing for a few days while http://babel-lutefisk.blogger.com, http://babel-lutefisk.net, and http://www.babel-lutefisk.net all resulted in a 404 Not Found error.

The really curious thing was that when pinging the domain names they all resolved to the same address, which is ghs.google.com. Anyway, without ranting too much about this (I am too happy to have my blog back) all credit and thanks goes to Chuck for his help.

Now I can get on with what I'm here for!

Thursday, 22 November 2007

WCF Client Proxies and Project Pre-Build events

Over the last couple of days I've had some fun setting up a new web application that speaks to a WCF web service. Both the web app (client) and the service are under development and are continually changing, and it became evident early on that updating the WCF service proxy on the client side was going to be pretty tedious.

The service proxy was originally set up using the Add Service Reference function in Visual Studio (I'm running VS2005 by the way). This is all fine and dandy, espcially since updating the proxy just requires a right-click and Update Service Reference, except it doesn't allow you to use any of the flags that the underlying svcutil tool supports via the command line. As a result the VS service reference function generates not only a service proxy but proxies for the classes that are passed between the client and service as well.

I wanted to set up something that would easily update the service proxy, omit proxy code for classes consumed or returned by the service, and allow the use of Generics in collections returned by the service. Using svcutil on the command line this can be accomplished by using the /reference and /collectionType flags. As I've already mentioned, the Add Service Reference function in VS2005 doesn't allow these flags, but you can get around this by using project pre-build events. I thought this might be useful to others out there so I've put together some instructions that may be of help.

!!Note: The code that follows will only work if you have added svcutil to your system path.

The basic setup is quite straight forward. In your solution, right-click your client project and select Properties. From the properties page select the Build Events tab. On this tab there are two text boxes in which to enter pre-build and post-build events on the command line. In the pre-build event command line box at the top, enter the following:


mkdir $(ProjectDir)SVC
chdir $(ProjectDir)SVC
svcutil http://MyServiceURL/MyService.svc /language:C# /out:MyServiceProxy.cs
copy MyServiceProxy.cs $(ProjectDir)"Service References"
chdir $(ProjectDir)
rmdir /S /Q $(ProjectDir)SVC


As you can see, this makes use of good old DOS-style commands. The first line creates a subdirectory in your project called SVC, and the second line changes the working directory to that same directory. Notice the use of the $(ProjectDir) shortcut/macro. If you click into Edit pre-build > Macros you'll see a list of the shortcuts VS makes available.

Now, the third line is what's of real interest. This calls svcutil referencing a running instance of the service (via a URL) for which I want to generate the proxy. It also specifies the output language (you can set this to VB if you like) and the filename that the proxy is written to. Then, on the last three lines the proxy class is copied into the "Service References" folder (which VS creates when you set up the Service Reference initially), and the temporary SVC folder is removed.

That's how simple it is! Now, there are two more things to accomplish, namely omitting proxy code for service return and parameter classes, and allowing the client to use Generics.

In this example the client and the service both reference a common class library that hold the DataContract classes used between services. Since this library is directly accessible to the client, it is not necessary to generate a service proxy containing DataContract proxies. To omit DataContract proxies, I simply add the /reference flag to the svcutil command like this:

/reference:$(TargetDir)MyCommonLibrary.dll

Now we're almost there. The last thing to do is to inform svcutil about the CollectionType(s) of the service. My particular service implements a couple of OperationContracts that return a List. If you don't tell svcutil this, the proxy will default to returning T[] instead. The flag to use is /collectionType (/ct is the short version), and this is how you do it:

/collectionType:System.Collections.Generic.List`1

Notice the single reverse quote before the "1" in that line, and don't confuse it with an apostrophe. By the way, the "1" signifies that Generic list has one type specified (List).

Now - there's one more thing to throw into the mix: Namespaces. You want the generated proxy to reside in the same namespace (or a related one) as your client. If you don't add the /namespace flag on svcutil the proxy generated will just contain a class and no specific namespace and that could pose a problem. But it, too, is easily fixed by adding

/namespace:*,MyNamespace

Putting all of that together the whole pre-build event looks something like this:


mkdir $(ProjectDir)SVC
chdir $(ProjectDir)SVC
svcutil http://MyServiceURL/MyService.svc /language:C# /out:MyServiceProxy.cs /reference:$(TargetDir)MyCommonLibrary.dll /collectionType:System.Collections.Generic.List`1 /namespace:*,MyNamespace
copy MyServiceProxy.cs $(ProjectDir)"Service References"
chdir $(ProjectDir)
rmdir /S /Q $(ProjectDir)SVC


Happy days! Now every time you build your client project it will look up the service and regenerate the proxy for you. That's pretty neat, I think.

A final word on the URL used to point to the running service instance. If you are sharing the client project with other developers (most likely you are) then the URL you've specified in the pre-build event needs to be accessible to the other developers as well. I get around this by setting the URL in the pre-build event to the URL for the service running on our development server, and overriding that URL in my local hosts file so that, while I develop on the client, the URL points to whereever I need it to.

Doing it this way also means that anyone can check out only the client project and it will automagically update the proxy reference against the running instance on the development server and thus pick up any changes committed by other developers working on the service!

Wednesday, 21 November 2007

Finally. Resurrection. Well, kind of..

It's 1:34am. I cannot sleep. Not for the life of me. Since my wife asked me to turn out the lights about two hours ago I've been tossing and turning. Not even listening to my iPod at the lowest volume setting worked (usually that knocks me out within minutes). So, since sleep escapes me I have decided that the time has come to get this blog going. That's why I'm sitting in the bathroom typing quietly so as not to wake my wife.

This was meant to be a resurrection of an older blog that I never got off the ground; dogspeed.blogspot.com. That blog didn't happen because I didn't really have anything to write about, and I got lazy quickly. Hopefully this one will not suffer the same fate as I believe I finally
do have something to write about; my work. Well - not the company I work for but what I do for a living. So this one's for the .Neters out there. Well, I might post other things, too - who knows?

Anyway, as I've pointed out it is very late and I should try and get some Zs. But here's to Babel Lutefisk
.net!