dcsimg
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 2 of 2

Thread: Need help understanding how hashing works

  1. #1
    Join Date
    Apr 2011
    Posts
    3

    Need help understanding how hashing works

    Hi,

    I want my program to have a login thingy. I also want all the data that i store to be hashed. So I got a sample program that converts cleartext strings into salted hashes with your algorithm of choice. But I don't understand it.

    When I apply a salted Sha512 hash to "myP@5sw0rd" it turns to this:

    YurSYks8cn605qoukD2v2026TkXFYBEvHJyk8nPDIFRgYbs+abRmRuW1Oz2qVrDV/+o5LtbS4fcx+B5DN304mUBsf30=

    But it could also turn to this:

    pZiEdJXMkJhRp9dAiiEJBDLv/dssR/BFKUyaEBcwmJ2SU+hjodxbvQlouOTThEbiAodvo1WQFNXGjKwNwEUZfrpELTuJ

    And it continues. Now I get why it turns out different. It is because the salted hash is different/random everytime. Anyway. How would you ever be able to compare this to another cleartext password?

    So the database has the SHA512 password. You fill in a password. It gets converted and salt added to it. How is it ever equal to the database when there is random salt added to it ? I don't get it AT ALL!

    Code:
    using System;
    using System.Text;
    using System.Security.Cryptography;
    
    /// <summary>
    /// This class generates and compares hashes using MD5, SHA1, SHA256, SHA384, 
    /// and SHA512 hashing algorithms. Before computing a hash, it appends a
    /// randomly generated salt to the plain text, and stores this salt appended
    /// to the result. To verify another plain text value against the given hash,
    /// this class will retrieve the salt value from the hash string and use it
    /// when computing a new hash of the plain text. Appending a salt value to
    /// the hash may not be the most efficient approach, so when using hashes in
    /// a real-life application, you may choose to store them separately. You may
    /// also opt to keep results as byte arrays instead of converting them into
    /// base64-encoded strings.
    /// </summary>
    public class SimpleHash
    {
        /// <summary>
        /// Generates a hash for the given plain text value and returns a
        /// base64-encoded result. Before the hash is computed, a random salt
        /// is generated and appended to the plain text. This salt is stored at
        /// the end of the hash value, so it can be used later for hash
        /// verification.
        /// </summary>
        /// <param name="plainText">
        /// Plaintext value to be hashed. The function does not check whether
        /// this parameter is null.
        /// </param>
        /// <param name="hashAlgorithm">
        /// Name of the hash algorithm. Allowed values are: "MD5", "SHA1",
        /// "SHA256", "SHA384", and "SHA512" (if any other value is specified
        /// MD5 hashing algorithm will be used). This value is case-insensitive.
        /// </param>
        /// <param name="saltBytes">
        /// Salt bytes. This parameter can be null, in which case a random salt
        /// value will be generated.
        /// </param>
        /// <returns>
        /// Hash value formatted as a base64-encoded string.
        /// </returns>
        public static string ComputeHash(string plainText,
                                         string hashAlgorithm,
                                         byte[] saltBytes)
        {
            // If salt is not specified, generate it on the fly.
            if (saltBytes == null)
            {
                // Define min and max salt sizes.
                int minSaltSize = 4;
                int maxSaltSize = 8;
    
                // Generate a random number for the size of the salt.
                Random random = new Random();
                int saltSize = random.Next(minSaltSize, maxSaltSize);
    
                // Allocate a byte array, which will hold the salt.
                saltBytes = new byte[saltSize];
    
                // Initialize a random number generator.
                RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    
                // Fill the salt with cryptographically strong byte values.
                rng.GetNonZeroBytes(saltBytes);
            }
    
            // Convert plain text into a byte array.
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
    
            // Allocate array, which will hold plain text and salt.
            byte[] plainTextWithSaltBytes =
                    new byte[plainTextBytes.Length + saltBytes.Length];
    
            // Copy plain text bytes into resulting array.
            for (int i = 0; i < plainTextBytes.Length; i++)
                plainTextWithSaltBytes[i] = plainTextBytes[i];
    
            // Append salt bytes to the resulting array.
            for (int i = 0; i < saltBytes.Length; i++)
                plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];
    
            // Because we support multiple hashing algorithms, we must define
            // hash object as a common (abstract) base class. We will specify the
            // actual hashing algorithm class later during object creation.
            HashAlgorithm hash;
    
            // Make sure hashing algorithm name is specified.
            if (hashAlgorithm == null)
                hashAlgorithm = "";
    
            // Initialize appropriate hashing algorithm class.
            switch (hashAlgorithm.ToUpper())
            {
                case "SHA1":
                    hash = new SHA1Managed();
                    break;
    
                case "SHA256":
                    hash = new SHA256Managed();
                    break;
    
                case "SHA384":
                    hash = new SHA384Managed();
                    break;
    
                case "SHA512":
                    hash = new SHA512Managed();
                    break;
    
                default:
                    hash = new MD5CryptoServiceProvider();
                    break;
            }
    
            // Compute hash value of our plain text with appended salt.
            byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
    
            // Create array which will hold hash and original salt bytes.
            byte[] hashWithSaltBytes = new byte[hashBytes.Length +
                                                saltBytes.Length];
    
            // Copy hash bytes into resulting array.
            for (int i = 0; i < hashBytes.Length; i++)
                hashWithSaltBytes[i] = hashBytes[i];
    
            // Append salt bytes to the result.
            for (int i = 0; i < saltBytes.Length; i++)
                hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];
    
            // Convert result into a base64-encoded string.
            string hashValue = Convert.ToBase64String(hashWithSaltBytes);
    
            // Return the result.
            return hashValue;
        }
    
        /// <summary>
        /// Compares a hash of the specified plain text value to a given hash
        /// value. Plain text is hashed with the same salt value as the original
        /// hash.
        /// </summary>
        /// <param name="plainText">
        /// Plain text to be verified against the specified hash. The function
        /// does not check whether this parameter is null.
        /// </param>
        /// <param name="hashAlgorithm">
        /// Name of the hash algorithm. Allowed values are: "MD5", "SHA1", 
        /// "SHA256", "SHA384", and "SHA512" (if any other value is specified,
        /// MD5 hashing algorithm will be used). This value is case-insensitive.
        /// </param>
        /// <param name="hashValue">
        /// Base64-encoded hash value produced by ComputeHash function. This value
        /// includes the original salt appended to it.
        /// </param>
        /// <returns>
        /// If computed hash mathes the specified hash the function the return
        /// value is true; otherwise, the function returns false.
        /// </returns>
        public static bool VerifyHash(string plainText,
                                      string hashAlgorithm,
                                      string hashValue)
        {
            // Convert base64-encoded hash value into a byte array.
            byte[] hashWithSaltBytes = Convert.FromBase64String(hashValue);
    
            // We must know size of hash (without salt).
            int hashSizeInBits, hashSizeInBytes;
    
            // Make sure that hashing algorithm name is specified.
            if (hashAlgorithm == null)
                hashAlgorithm = "";
    
            // Size of hash is based on the specified algorithm.
            switch (hashAlgorithm.ToUpper())
            {
                case "SHA1":
                    hashSizeInBits = 160;
                    break;
    
                case "SHA256":
                    hashSizeInBits = 256;
                    break;
    
                case "SHA384":
                    hashSizeInBits = 384;
                    break;
    
                case "SHA512":
                    hashSizeInBits = 512;
                    break;
    
                default: // Must be MD5
                    hashSizeInBits = 128;
                    break;
            }
    
            // Convert size of hash from bits to bytes.
            hashSizeInBytes = hashSizeInBits / 8;
    
            // Make sure that the specified hash value is long enough.
            if (hashWithSaltBytes.Length < hashSizeInBytes)
                return false;
    
            // Allocate array to hold original salt bytes retrieved from hash.
            byte[] saltBytes = new byte[hashWithSaltBytes.Length -
                                        hashSizeInBytes];
    
            // Copy salt from the end of the hash to the new array.
            for (int i = 0; i < saltBytes.Length; i++)
                saltBytes[i] = hashWithSaltBytes[hashSizeInBytes + i];
    
            // Compute a new hash string.
            string expectedHashString =
                        ComputeHash(plainText, hashAlgorithm, saltBytes);
    
            // If the computed hash matches the specified hash,
            // the plain text value must be correct.
            return (hashValue == expectedHashString);
        }
    }
    Code:
                string password      = "myP@5sw0rd";  // original password
                string wrongPassword = "password";    // wrong password
     
                string passwordHashMD5 = SimpleHash.ComputeHash(password, "MD5", null);
                string passwordHashSha1 = SimpleHash.ComputeHash(password, "SHA1", null);
                string passwordHashSha256 = SimpleHash.ComputeHash(password, "SHA256", null);
                string passwordHashSha384 = SimpleHash.ComputeHash(password, "SHA384", null);
                string passwordHashSha512 = SimpleHash.ComputeHash(password, "SHA512", null);
    
                textBox1.Text=passwordHashSha512;
                //Application.Exit();
                Console.WriteLine("COMPUTING HASH VALUES\r\n");
                Console.WriteLine("MD5   : {0}", passwordHashMD5);
                Console.WriteLine("SHA1  : {0}", passwordHashSha1);
                Console.WriteLine("SHA256: {0}", passwordHashSha256);
                Console.WriteLine("SHA384: {0}", passwordHashSha384);
                Console.WriteLine("SHA512: {0}", passwordHashSha512);
                Console.WriteLine("");
    
                Console.WriteLine("COMPARING PASSWORD HASHES\r\n");
                Console.WriteLine("MD5    (good): {0}",
                                SimpleHash.VerifyHash(
                                password, "MD5", 
                                passwordHashMD5).ToString());
                Console.WriteLine("MD5    (bad) : {0}",
                                SimpleHash.VerifyHash(
                                wrongPassword, "MD5", 
                                passwordHashMD5).ToString());
                Console.WriteLine("SHA1   (good): {0}",
                                SimpleHash.VerifyHash(
                                password, "SHA1", 
                                passwordHashSha1).ToString());
                Console.WriteLine("SHA1   (bad) : {0}",
                                SimpleHash.VerifyHash(
                                wrongPassword, "SHA1", 
                                passwordHashSha1).ToString());
                Console.WriteLine("SHA256 (good): {0}",
                                SimpleHash.VerifyHash(
                                password, "SHA256", 
                                passwordHashSha256).ToString());
                Console.WriteLine("SHA256 (bad) : {0}",
                                SimpleHash.VerifyHash(
                                wrongPassword, "SHA256", 
                                passwordHashSha256).ToString());
                Console.WriteLine("SHA384 (good): {0}",
                                SimpleHash.VerifyHash(
                                password, "SHA384", 
                                passwordHashSha384).ToString());
                Console.WriteLine("SHA384 (bad) : {0}", 
                                SimpleHash.VerifyHash(
                                wrongPassword, "SHA384", 
                                passwordHashSha384).ToString());
                Console.WriteLine("SHA512 (good): {0}",
                                SimpleHash.VerifyHash(
                                password, "SHA512", 
                                passwordHashSha512).ToString());
                Console.WriteLine("SHA512 (bad) : {0}",
                                SimpleHash.VerifyHash(
                                wrongPassword, "SHA512", 
                                passwordHashSha512).ToString());

  2. #2
    Join Date
    Feb 2011
    Location
    United States
    Posts
    1,016

    Re: Need help understanding how hashing works

    Long story short: Salts protect against an attack to recover the plaintext wherein the attacker would pre-compute all the hashes of strings (in the dictionary, for example) and then use a table-lookup to recover the original plaintext. Salts prevent this by increasing the amount of space required to store those tables (to impractical levels).

    Above, the hash values contain the salt in their output. Thus if you computed the hash for 'pass' the output would be salt + hash('pass', salt) (where + means 'append'). This could then be stored in a database. When I wanted to later find out if you have given me the same password, I would compute the hash of the password you just gave me USING the salt stored in the database for that hash value and then compare for equality.

    So to summarize: the salt is random when you generate the hash the first time for storage, but is NOT random (and encoded in the result you get out of the hash function to start with) when you go to compare later.

    Does that make sense? If not, feel free to ask more questions.

    More information at: http://en.wikipedia.org/wiki/Salt_&#37;28cryptography%29
    Best Regards,

    BioPhysEngr
    http://blog.biophysengr.net
    --
    All advice is offered in good faith only. You are ultimately responsible for effects of your programs and the integrity of the machines they run on.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width




On-Demand Webinars (sponsored)