I have a simple login windows application. I have successfully created a SQL database in the localhost which contains user information like Username, Password(hashed), Salt and Email, and I have also successfully created a user registration form where a new user can input their details and these details will be added to the database, it works fine.
But I've unable to validate the Username and Password against the stored values in the row containing the user data in the database(localhost) to give access.
I've able to write hashing, salting and validation methods for further use.
HashSalt.cs class
// --- constructor to initialize num_of_iterations
public HashSalt(int numOfIterations)
{
num_of_iterations = numOfIterations;
}
// --- Generate Salt ---
public string generateSalt()
{
var salt = new byte[32];
var randomProvider = new RNGCryptoServiceProvider();
randomProvider.GetBytes(salt);
return Convert.ToBase64String(salt); // returns salt as a string
}
// --- converts salt string into byte[]
public byte[] saltToByte(string salt)
{
var byteSalt = Convert.FromBase64String(salt);
return byteSalt;
}
// --- Generate hash of(pass+salt) ---
public string generateHash(string password, byte[] salt)
{
var rfc2898 = new Rfc2898DeriveBytes(password, salt, num_of_iterations);
var Password = rfc2898.GetBytes(32); // gives 32 byte encoded password
return Convert.ToBase64String(Password); // returns hash
}
// --- Authenticate User ---
public bool AuthenticateUser(string enteredPassword, string storedHash, string storedSalt)
{
var saltBytes = Convert.FromBase64String(storedSalt);
var rfc2898DeriveBytes = new Rfc2898DeriveBytes(enteredPassword, saltBytes, 10000);
return Convert.ToBase64String(rfc2898DeriveBytes.GetBytes(256)) == storedHash;
}
}
}
And this is my Login.cs for login form
private void btnSignin_Click(object sender, EventArgs e)
{
string enteredPass = txtLoginPassword.Text;
DbHandler db = new DbHandler();
MySqlDataAdapter adapter = new MySqlDataAdapter();
DataTable table = new DataTable();
MySqlCommand cmd = new MySqlCommand("SELECT password, salt FROM student WHERE indexno=@index;", db.getConnection());
db.openConnection(); // open connection
cmd.Parameters.Add("@index", MySqlDbType.VarChar).Value = txtLoginusername.Text;
adapter.SelectCommand = cmd;
adapter.Fill(table);
if (table.Rows.Count > 0)
{
string pass = table.Rows[0][0].ToString();
string salt = table.Rows[0][1].ToString();
string newPass = hashSalt.generateHash(enteredPass, hashSalt.saltToByte(salt));
if (hashSalt.authenticateUser(enteredPass, pass, salt))
{
// MessageBox.Show("correct ='" + pass + "'\nEntered pass ='" + hashSalt.authenticateUser(enteredPass, pass, salt) + "'");
// --- form Records obj
Records gotoRecords = new Records();
gotoRecords.Show(); // goto Records
this.Hide();
}
else
{
string message = "User name & Password did not Match!?";
string title = "Attention!";
MessageBoxButtons buttons = MessageBoxButtons.OK;
DialogResult result = MessageBox.Show(message, title, buttons, MessageBoxIcon.Warning);
}
}
else
{
string message = "User name NotFound!?";
string title = "Attention!";
MessageBoxButtons buttons = MessageBoxButtons.OK;
DialogResult result = MessageBox.Show(message, title, buttons, MessageBoxIcon.Warning);
}
//this.Close();
db.openConnection(); // close connection
Database Structure
I would greatly appreciate it if you kindly give me some instructions to how to do that.
I think the problem is with the datatype of salt column in your mysql table. Here,
string newPass = hashSalt.generateHash(enteredPass, hashSalt.saltToByte(salt));
you're passing salt
getting from database to saltToByte()
method to convert into
byte array
, so I think you're planned to store your salt as a string using return value of generateSalt()
method. And reconverting it to byte array
using generateHash()
method when you need to authenticate a user.
This is a messy implementation.
Recommend you to store your salt as a byte array
so you don't need to use saltToByte()
.
Additioned to that,
your byte keys are different in generateSalt()
byte key set to
var salt = new byte[32];
32-bytekey and in AuthenticateUser()
you're using 256-byte key rfc2898DeriveBytes.GetBytes(256)