About Profiles Site Code Migration to Commerce Server 2007

For the latest version of Commerce Server 2007 Help, see the Microsoft Web site.

To migrate your Profiles System site code and site data to Commerce Server 2007, you must change the site code for password validation.

The hash function in Commerce Server 2007 uses SHA256. Earlier versions of Commerce Server use MD5. If you have user profiles, you might have to change the site code that validates passwords.

If you use the Commerce Server membership provider to authenticate users, you do not have to change your site code to support the SHA256 hash function. The Commerce Server membership provider supports both hash functions.

If you do not use the Commerce Server membership provider to authenticate users, and if you use one-way hashing to encrypt passwords, you must modify the site code that validates passwords. To validate a password, your code should use both the MD5 hash function and the SHA256 hash function.

Example

The following example illustrates how the Commerce Server membership provider validates passwords by using both the MD5 hash function and the SHA256 hash function.

public bool ValidatePassword(string password, string userId)
{
    // Get the password stored in the user profile.
    Profile profile = CommerceContext.Current.ProfileSystem.GetProfile(userId, "UserObject");
    string profilePassword = (string)profile["GeneralInfo.user_security_password"].Value;

    // Remove the salt value from the front of the stored password.
    string saltValue = profilePassword.Substring(0, SaltValueSize * UnicodeEncoding.CharSize);

    // Hash the incoming clear-text password using the salt value.
    string hashedInputPassword = HashPasswordSHA256(password, saltValue);

    // Compare the strings by using a standard string comparison. 
    // Be Aware That this kind of comparison is case sensitive.  

    if (profilePassword == hashedInputPassword)
    {
        return true;
    }

    // Check to see whether the password has been hashed
    // using the legacy MD5 algorithm.
    hashedInputPassword = HashPasswordMD5(password, saltValue);
    if (profilePassword == hashedInputPassword)
    {
        return true;
    }

    return false;
}

private static string HashPasswordSHA256(string clearPassword, string saltValue)
{
    return HashPassword(clearPassword, saltValue, new SHA256Managed());
    }

private static string HashPasswordMD5(string clearPassword, string saltValue)
{
    return HashPassword(clearPassword, saltValue, new MD5CryptoServiceProvider());
}

public const int SaltValueSize = 4;

private static string HashPassword(string clearPassword, string saltValue, HashAlgorithm hash)
    {
    UnicodeEncoding encoding = new UnicodeEncoding();

    if (clearPassword != null && hash != null && encoding != null)
        {
        // If the salt string is null or the length is not valid, 
        // create a new salt value.

        if (saltValue == null)
            {
            // Generate a salt string.
            saltValue = GenerateSaltValue();
            }

        // Convert the salt string and the password string to a
        // single array of bytes. Be aware that the password string is
        // Unicode and therefore may not have a zero in
        // every other byte.

        byte[] binarySaltValue = 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);

        byte[] valueToHash = new byte[SaltValueSize + encoding.GetByteCount(clearPassword)];
        byte[] binaryPassword = encoding.GetBytes(clearPassword);

        // Copy the salt value and the password to the hash buffer.

        binarySaltValue.CopyTo(valueToHash, 0);
        binaryPassword.CopyTo(valueToHash, SaltValueSize);

        byte[] hashValue = hash.ComputeHash(valueToHash);

        // The hashed password is the salt + the hash value (as a
        // string).

        string hashedPassword = saltValue;

        foreach (byte hexdigit in hashValue)
            {
            hashedPassword += hexdigit.ToString("X2", CultureInfo.InvariantCulture.NumberFormat);
            }

        // Return the hashed password as a string.

        return hashedPassword;
        }

    return null;
}

private static string GenerateSaltValue()
{
    UnicodeEncoding utf16 = new UnicodeEncoding();

    if (utf16 != null)
        {
        // Create a random number object seeded from the value
        // of the last random seed value. This is 
        // interlocked because it is a static value and you want
        // it to roll forward safely.

        Random random = new Random(unchecked((int)DateTime.Now.Ticks));

        if (random != null)
            {
            // Create an array of random values.

            byte[] saltValue = new byte[SaltValueSize];

            random.NextBytes(saltValue);

            // Convert the salt value to a string. Be aware that the
            // resulting string will still be an array of binary
            // values and not a printable string. Also, it does 
            // not convert each byte to a double byte.

            string saltValueString = utf16.GetString(saltValue);

            // Return the salt value as a string.

            return saltValueString;
            }
        }

    return null;
}

See Also

Other Resources

About Migrating ASP.NET-based Commerce Server 2002 Applications to Commerce Server 2007