Search code examples
c#stringhexbyteformatexception

byte.Parse (c#) : FormatException on Hexadecimal string


I'm actually trying to implement a very simple login mecanism for an app I'm developping in Visual C# .NET 2.0 on an embedded device. After some researches, I've found on the msdn a code sample performing password hashing :

How to store passwords

Unfortunately, when I try to use it, that code sample is raising a FormatException on the call to byte.Parse on the substrings of the hexadecimal string SaltValue. I really have trouble to understand why, since I haven't done any change to the code.

Here is the code :

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

private const int SaltValueSize = 4;

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 done
    // interlocked because it is a static value and we 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. Note 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.


        //Original line :
        //string saltValueString = utf16.GetString(saltValue);
        //Replaced by :
        string saltValueString = utf16.GetString(saltValue, 0, SaltValueSize);

        // Return the salt value as a string.

        return saltValueString;
    }
}

return null;
}

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

if (clearData != null && hash != null && encoding != null)
{
    // If the salt string is null or the length is invalid then
    // create a new valid 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. Note that the password string is Unicode and
    // therefore may or may not have a zero in every other byte.

    byte[] binarySaltValue = new byte[SaltValueSize];

    //FormatException raised here
    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);

//...
//Some more code
//...
}
}

I only have changed one line :

string saltValueString = utf16.GetString(saltValue);

to

string saltValueString = utf16.GetString(saltValue, 0, SaltValueSize);

because the first version of the method doesn't seem to be available for embedded C#. But anyway I've tested without changing this line (on a non-embedded environment), and it still was raising a FormatException.

I've copied the SaltValueSize value from that other msdn code sample (which is related) : How to validate passwords

The test that raises the exception :

HashPassword("youpi", null, new SHA1CryptoServiceProvider());


Solution

  • The problem lies in the fact that your GenerateSaltValue method does not return string of hexademical numbers.

    It returns string of some random symbols, that may or usually may not be valid hexademical symbols - for me it created string of mostly Chinese hieroglyphs that for sure aren't parseable by Byte.Parse method.

    Also, your example pertains to Microsoft Commerce Server - I have no idea whatsoever it is.

    "SOLUTION:"

    I am not sure what all this examples wants to accomplish with this string-tohex-tobinary conversions, but for it to successfully execute the GenerateSaltValue should be something like:

    public static string ByteArrayToString(byte[] byteArray)
    {
        StringBuilder hex = new StringBuilder(byteArray.Length * 2);
    
        foreach (byte b in byteArray)
            hex.AppendFormat("{0:x2}", b);
    
        return hex.ToString();
    }
    
    // Renamed GenerateSaltValue method
    private static string GenerateHexSaltString()
    {
        Random random = new Random();
    
        // Create an array of random values.
        byte[] saltValue = new byte[SaltValueSize];
    
        random.NextBytes(saltValue);
    
        string saltValueString = ByteArrayToString(saltValue);
    
        // Return the salt value as a string.
        return saltValueString;
    }
    

    And your program will "work", thanks to How do you convert Byte Array to Hexadecimal String, and vice versa?

    BUT:

    • Using Random for Salt creation is a bad idea.
    • string-tohex-tobinary conversion looks even worser.
    • And other problems...

    SO:

    Read some articles that really pertains to C# password hashing and encryption, like:

    Hash and salt passwords in C#

    And be very attentive while searching for code examples - they could use another version, platform or even language. Good luck.