Link to home
Start Free TrialLog in
Avatar of Brian
BrianFlag for United States of America

asked on

Encrypt Password using MD5CyptoServiceProvider Class

Hello Experts,

I would like to know if someone could help me with my code attached? I need help performing a one way encryption on my password using the salting+hash technique when a user creates a new account.

I'm also not sure what the field type(s) should be for the users_password and users_password_salt fields.

Thanks in advance!!!



I also
Avatar of kaufmed
kaufmed
Flag of United States of America image

Avatar of Brian

ASKER

Hi kaufmed,

I'm game for whatever you think is better to use then rather than MD5. I was not aware of that, hence just learning this sort of stuff.

Whatever you decide would you be willing to help me adjust my code to whatever you choose?

Thanks in advance!!!
Avatar of Brian

ASKER

Hi kaufmed,

Ok, I looked into the different Hash Function for .NET and found that SHA-512 is the best you can use. So I modified my code and was wondering if you would be willing to look over it and let me know if it looks good to you and if not what I should changed.

Thanks in advance!!!


protected void btn_NewUser_Click(object sender, EventArgs e)
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
        {
            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "HealthCourses_InsertUsers";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = conn;

            cmd.Parameters.AddWithValue("@c_id", SqlDbType.Int).Value = ddlClient.SelectedItem.Value;
            cmd.Parameters.AddWithValue("@bldg_id", SqlDbType.Int).Value = ddlBldgLocation.SelectedItem.Value;
            cmd.Parameters.AddWithValue("@s_id", SqlDbType.Int).Value = ddlState.SelectedItem.Value;
            cmd.Parameters.AddWithValue("@users_flname", SqlDbType.VarChar).Value = txtName.Text;
            cmd.Parameters.AddWithValue("@users_street_address", SqlDbType.VarChar).Value = txtHomeAddress.Text;
            cmd.Parameters.AddWithValue("@users_city", SqlDbType.VarChar).Value = txtCity.Text;
            cmd.Parameters.AddWithValue("@users_zip", SqlDbType.VarChar).Value = txtZipCode.Text;
            cmd.Parameters.AddWithValue("@users_phone", SqlDbType.VarChar).Value = txtHomePhone.Text;
            cmd.Parameters.AddWithValue("@users_work_ext", SqlDbType.VarChar).Value = txtWorkExtension.Text;
            cmd.Parameters.AddWithValue("@users_email", SqlDbType.VarChar).Value = txtEmailAddress.Text;
            cmd.Parameters.AddWithValue("@users_username", SqlDbType.VarChar).Value = txtUserName.Text;

            //Encrypt the password
            SHA512CryptoServiceProvider SHA512Hash = new SHA512CryptoServiceProvider();

            byte[] hashedBytes = null;
            UTF8Encoding encoder = new UTF8Encoding();

            string guid = System.Guid.NewGuid().ToString(); //36 long characters
            hashedBytes = SHA512Hash.ComputeHash(encoder.GetBytes(txtPassword.Text + guid));

            cmd.Parameters.AddWithValue("@users_password", SqlDbType.VarChar).Value = hashedBytes;
            cmd.Parameters.AddWithValue("@users_password_salt", SqlDbType.VarChar).Value = guid;

            try
            {
                conn.Open();

                cmd.ExecuteNonQuery();

                Response.Redirect("newuser_success.aspx");
            }

            catch (Exception ex)
            {
                lblInsertError.Text = ex.Message.ToString();
            }

            finally
            {
                conn.Close();
            }
        }
    }

Open in new window

I know you said that you wanted a one-way hash, and I believe you have it, but are you planning on comparing hashes at some later point? If so, the call to NewGuid is going to give you a completely different (1 in trillions chance of repetition, I believe; most likely a lower chance) salt value each time you call it. Since you appear to be salting your password value, you will never be able to compare an incoming password with the database version since the salt will be different each time.

Perhaps I misinterpret your intent, though.
Avatar of Brian

ASKER

I'm not sure what you mean. I was under the impression that it is recommended that you HASH your password in it's own field in the DB and then add the SALT value to the password in another field in the DB.

So once again I thought I needed to use SHA512 to encrypt/hash the password in Field A but then I also thought I have to create a SALT value in Field B. So that when the users try to login you compane Field A and Field B and if true authenticate.

Perhaps I'm missing something or doing something more than what is needed.

I was under the impression that it is recommended that you HASH your password in it's own field in the DB and then add the SALT value to the password in another field in the DB.
That is correct, to my knowledge. But think about this:  why would one want to hash a password? When a user uses your page or your form to enter his password, does he enter the hash or does he enter the password? You hash a password so that intruders don't get the actual passwords of your users. When you accept a user's password via data entry, you hash the incoming data using the same method (and same salt!) that you used when you inserted it into the database. If you were not hashing, and an intruder got the clear-text passwords from your database, then he could simply log into your application or web site using normal credentials. If you hash your passwords, then having the hash does an intruder no good because you be running the text received via data entry through your hash algorithm again and comparing that hash to the hash that is stored in the database. If an intruder submits the hashed value, then your comparison is then the hash on the database to the hash of the hash of the password, which for all intents and purposes will never be the same.

The fact that you call NewGuid without keeping track of that generated GUID means that when you hash the incoming text from the user, you will never be using the same salt. Different salt == different hash.
Disregard my last comment regarding the GUID. I missed the line in your code where you are storing that value. The very last statement holds true, though.
Avatar of Brian

ASKER

Hi kaufmed,

Ok, I'm confused now. Is the reasons that you HASH and SALT using two different fields to begin with is to compare the two fields when a user attempts to login? Please take a look at my attached .txt file it contains all code and stored procedures used. Everytihng works fine, but I just want someone else to make sure that the coding i'm using for HASHING is correct. It just seems very easy and not much involved to HASH a password and I'm worried that I"m leaving something out.

 EE.txt
Please feel free to hold out for responses from those who are more security-proficient as I cannot safely claim to be. I am expressing what I understand hash and salt to be used for.

A salt is just some value that you append (or prepend or insert) into the value you want to encrypt. You do this because without it, it is a tad easier to brute-force the guessing of passwords. This can be accomplished using rainbow tables, among other things. When you add a salt, it causes a value that would ordinarily hash to some arbitrary value to hash to some other arbitrary value.

Here's a real example using your selected algorithm.

using System;
using System.Security.Cryptography;
using System.Text;

namespace _27406871
{
    class Program
    {
        static void Main(string[] args)
        {
            string toHash = "Hello World";
            SHA512CryptoServiceProvider hasher = new SHA512CryptoServiceProvider();
            Encoding encoding = System.Text.UTF8Encoding.UTF8;
            byte[] unsaltedBytes = encoding.GetBytes(toHash);
            byte[] saltedBytes = encoding.GetBytes(toHash + "some_arbitrary_value");
            byte[] saltedBytes2 = encoding.GetBytes(toHash + "some_other_arbitrary_value");

            byte[] unsaltedHash = hasher.ComputeHash(unsaltedBytes);
            byte[] saltedHash = hasher.ComputeHash(saltedBytes);
            byte[] saltedHash2 = hasher.ComputeHash(saltedBytes2);

            string unsaltedHashString = encoding.GetString(unsaltedHash);
            string saltedHashString = encoding.GetString(saltedHash);
            string saltedHashString2 = encoding.GetString(saltedHash2);

            Console.WriteLine("Unsalted Hash:");
            Console.WriteLine(unsaltedHashString);
            Console.WriteLine("\nSalted Hash:");
            Console.WriteLine(saltedHashString);
            Console.WriteLine("\nSalted Hash 2:");
            Console.WriteLine(saltedHashString2);
            Console.ReadKey();
        }
    }
}

Open in new window


If you ran the above, you would see three different outputs. This is what the salt gives you. If an intruder tried to brute force passwords against your encryption, they wouldn't work because of the addition of salt.

Adding salt to a value before encryption gives you a different value than not having salt does. If you didn't use salt, then there would be nothing more you had to do when you compared your two values (the hashed DB value and the incoming user-supplied text, which you would run through your hashing algorithm). However, if you use salt, then you must keep that salt in order to be able to compare the test coming from your user at a later date.

Let's assume you are doing this for a login screen. As you saw above, if you hashed your original value using a salt, then you would get a particular value. Let's pretend you stored this value to the database. You user knows absolutely nothing about the salt--this is something for you (your computer) alone. The user will enter his password to log in to your site. When he clicks the submit button, his browser sends this password (clear text) to your site. Your site will attempt to hash the password. Now, if you hash the password with no salt, then you will get some arbitrary value, and this value will not match the value stored in your database, even if it is the correct password. You must hash the incoming password using the same salt that you used when you inserted the original hashed password into the database. The reason you store both values is simple:

You store the hash for comparison.
You store the salt to hash the password the user submitted via his browser.

If you hash the incoming password using the salt you stored in the database, then the resulting value should be the same as what you stored in the hash field when you originally inserted it into the database (provided the user entered the correct password and you didn't change your hashing algorithm or the salt).
Avatar of Brian

ASKER

Confused by all that. Im not sure what I'm missing. Do I need to use salt if I'm hashing using sha512? If so do I or should I include the hash and salt into the same database field rather than having two fields in my database?
Do I need to use salt if I'm hashing using sha512?
I don't see how it would hurt.

If so do I or should I include the hash and salt into the same database field rather than having two fields in my database?
No. Keep it separate because you will need the hash again.
No. Keep it separate because you will need the hash again.
Sorry. I meant to say, "No. Keep it separate because you will need the salt again."
Avatar of Brian

ASKER

Ok, so is there something wrong with my code in the way that I'm storing the hash/salt values?

is there something wrong with how I authenticate using my attached login code?

I just want to make sure my code is good. If you are not 100% sure then how can I get more input on this post?
If you are not 100% sure then how can I get more input on this post?
"Request Attention" for this question and ask that an expert alert be sent out.
untitled.PNG
Avatar of Brian

ASKER

Hi kaufmed,

Please do not take that personal. I just want to make sure that I'm handling everything properly. If not then I would like someone to tell me what I need to change in my code. Hopefully it does not take a long time to get a response :(
Please do not take that personal.
Not at all. That's why I made a point of telling you that I am not a security expert  = )

Besides, it vicariously gives me a chance to confirm whether or not I know what I'm talking about  ; )
You would want to add salt to the password then hash it and store the resulting hash along with the salt in your database. That way the next time the user inputs their password you add the salt from the database, hash it. Then compare the hash results with those stored in the database. You always want to hash with salt, so the cracker would have to generate new rainbow tables for each entry based off of the salt.(Much more time consuming and expensive)

Below I use two functions, one to create a random salt which I hash and encode to base 64. Then the other function takes the typed password and salt and returns a hashed value. These could be modified for database use or however you need. You would want to store in a varchar(max) datatype since it is base64 text encoded.

Hope this helps,
Dustin
using System.Security.Cryptography;
public class PassEnc
{
	public static string SHA512_CreateSalt()
	{
		object minSize = 64;
		object maxSize = 128;
		Random rndom = new Random();
		Int32 saltsize = rndom.Next(minSize, maxSize);
		RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
		byte[] saltBytes = new byte[saltsize];
		rng.GetNonZeroBytes(saltBytes);
		SHA512Managed sha = new SHA512Managed();
		ASCIIEncoding ae = new ASCIIEncoding();
		byte[] Hash = sha.ComputeHash(saltBytes);
		return Convert.ToBase64String(Hash);
	}
	public static string SHA512_Encrypt(string Txt, string salt)
	{
		// Convert plain text into a byte array.
		byte[] plainTextBytes = Encoding.UTF8.GetBytes(Txt);
		// Convert salt into a byte array from Base64
		byte[] saltbytes = Convert.FromBase64String(salt);
		// Allocate array, which will hold plain text and salt.
		byte[] plainTextWithSaltBytes = new byte[plainTextBytes.Length + (saltbytes.Length - 1) + 1];
		// Copy plain text bytes into resulting array.
		for (int i = 0; i <= plainTextBytes.Length - 1; i++) {
			plainTextWithSaltBytes(i) = plainTextBytes(i);
		}
		// Append salt bytes to the resulting array.
		for (int i = 0; i <= saltbytes.Length - 1; i++) {
			plainTextWithSaltBytes(plainTextBytes.Length + i) = saltbytes(i);
		}
		//Define cryptographic algorithm
		SHA512Managed sha = new SHA512Managed();
		//Define and fill hash
		byte[] Hash = sha.ComputeHash(plainTextWithSaltBytes);
		//Return hash as Base64
		return Convert.ToBase64String(Hash);

	}
}

Open in new window

Avatar of Brian

ASKER

Hi Dustin,

Ok, first of all let me thank you for choosing to help out with this post. It means alot to me and is a big part before I can finish my project not to mention the most important part which is signing up users but protecting their data.

Please forgive me if I repeat what you mentioned but I'm not as knowledable when it comes to this stuff but I have been trying very hard to learn it. I really want to learn this stuff and it's very important to my job, but I most importantly I really want to understand what it means and not just copy/paste.

I looked at the .NET Class Library and what I got from that was the following below. Please keep in mind you and kaufmed are both saying the same thing in regards to using Salt + Hash and I just want someone to look over my code to make sure I did it correctly and if not to tell me what it is that I need to fix and why.

I'm going to attach my current code that "Inserts data" which as you will see I'm trying to Salt + Hash the Password and I'm storing the Password into a field called "users_password" and then storing the Password Salt into a field called "users_password_salt". I'm not sure if I'm doing this correctly but please see my attached code for creating a new user and then logging that user in against his/her credentials.

Note: All of the code that I just attached works perfectly fine and I have no errors/problems at all I just want to make sure that I'm Salting and Hashing the Password correctlyl and storing it in the Database properly.

Database Field Names / Field Types:

users_password  varchar(50)
users_password_salt  varchar(50)



Thanks in advance!!!
CREATE USER CODE:

protected void btn_NewUser_Click(object sender, EventArgs e)
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
        {
            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "HealthCourses_InsertUsers";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = conn;

            cmd.Parameters.AddWithValue("@c_id", SqlDbType.Int).Value = ddlClient.SelectedItem.Value;
            cmd.Parameters.AddWithValue("@bldg_id", SqlDbType.Int).Value = ddlBldgLocation.SelectedItem.Value;
            cmd.Parameters.AddWithValue("@s_id", SqlDbType.Int).Value = ddlState.SelectedItem.Value;
            cmd.Parameters.AddWithValue("@users_flname", SqlDbType.VarChar).Value = txtName.Text;
            cmd.Parameters.AddWithValue("@users_street_address", SqlDbType.VarChar).Value = txtHomeAddress.Text;
            cmd.Parameters.AddWithValue("@users_city", SqlDbType.VarChar).Value = txtCity.Text;
            cmd.Parameters.AddWithValue("@users_zip", SqlDbType.VarChar).Value = txtZipCode.Text;
            cmd.Parameters.AddWithValue("@users_phone", SqlDbType.VarChar).Value = txtHomePhone.Text;
            cmd.Parameters.AddWithValue("@users_work_ext", SqlDbType.VarChar).Value = txtWorkExtension.Text;
            cmd.Parameters.AddWithValue("@users_email", SqlDbType.VarChar).Value = txtEmailAddress.Text;
            cmd.Parameters.AddWithValue("@users_username", SqlDbType.VarChar).Value = txtUserName.Text;

            //Create instance of SHA512CryptoServiceProvider
            SHA512CryptoServiceProvider SHA512Hash = new SHA512CryptoServiceProvider();

            byte[] hashedBytes = null;
            UTF8Encoding encoder = new UTF8Encoding();

            string guid = System.Guid.NewGuid().ToString(); //36 long characters
            hashedBytes = SHA512Hash.ComputeHash(encoder.GetBytes(txtPassword.Text + guid));

            cmd.Parameters.AddWithValue("@users_password", SqlDbType.VarChar).Value = hashedBytes;
            cmd.Parameters.AddWithValue("@users_password_salt", SqlDbType.VarChar).Value = guid;

            try
            {
                conn.Open();

                cmd.ExecuteNonQuery();

                Response.Redirect("newuser_success.aspx");
            }

            catch (Exception ex)
            {
                lblInsertError.Text = ex.Message.ToString();
            }

            finally
            {
                conn.Close();
            }
        }
    }





LOGIN CODE:

protected void btn_ProgramInfoSignIn_Click(object sender, EventArgs e)
    {
        //Retrieve the guid from db
        string guid = String.Empty;

        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
        {
            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "HealthCourses_LoginPassSalt";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = conn;

            cmd.Parameters.AddWithValue("@users_username", SqlDbType.VarChar).Value = txtUserName.Text;

            DataTable dtGuid = new DataTable();

            SqlDataAdapter adp = new SqlDataAdapter();
            adp.SelectCommand = cmd;
            adp.Fill(dtGuid);

            if (dtGuid != null && dtGuid.Rows.Count > 0)
            {
                guid = dtGuid.Rows[0]["users_password_salt"].ToString();

                SqlCommand cmdClientLogin = new SqlCommand();
                cmdClientLogin.CommandText = "HealthCourses_Login";
                cmdClientLogin.CommandType = CommandType.StoredProcedure;
                cmdClientLogin.Connection = conn;

                //Create instance of SHA512CryptoServiceProvider
                SHA512CryptoServiceProvider SHA512Hash = new SHA512CryptoServiceProvider();
                byte[] hashedBytes = null;
                UTF8Encoding encoder = new UTF8Encoding();
                hashedBytes = SHA512Hash.ComputeHash(encoder.GetBytes(txtPassword.Text + guid));

                cmdClientLogin.Parameters.AddWithValue("@users_username", SqlDbType.VarChar).Value = txtUserName.Text;
                cmdClientLogin.Parameters.AddWithValue("@users_password", SqlDbType.VarChar).Value = hashedBytes;

                conn.Open();

                SqlDataReader rdr = cmdClientLogin.ExecuteReader();

                if (rdr.HasRows && rdr.Read())
                {
                    rdr.Close();
                    conn.Close();
                    Session["UserNameSessionID"] = txtUserName.Text;
                    FormsAuthentication.RedirectFromLoginPage(txtPassword.Text, false);
                }
            }

            else
            {
                lblSignInError.Text = "Invalid Credentials!";
            }
        }
    }

Open in new window

Avatar of Brian

ASKER

@dusion:

Are you still able to assist?
ASKER CERTIFIED SOLUTION
Avatar of Dustin Hopkins
Dustin Hopkins
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial