简体   繁体   English

如何将哈希密码与普通密码进行比较?

[英]How to compare the hash-password with ordinary one?

I have big trouble in verifying the hash password from the database with the ordinary one coming from the login page. 在使用来自登录页面的普通密码验证数据库中的哈希密码时,我遇到了很大的麻烦。 How to validate a user by comparing this two passwords.Here is my code for registration page: 如何通过比较这两个密码来验证用户。这是我的注册页面代码:

protected void Button1_Click(object sender, EventArgs e)
{

    SHA384CryptoServiceProvider sh = new SHA384CryptoServiceProvider();
    byte[] plainbytes = Encoding.ASCII.GetBytes(TextBox2.Text);
    var sha = sh.ComputeHash(plainbytes);
    byte[] hashbytes = sh.Hash;
    SqlConnection con = new SqlConnection(constr);
    SqlCommand cmd = new SqlCommand("RegisterUser",con);
    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter param = null;
    param = cmd.Parameters.Add("@username",SqlDbType.VarChar,10);
    param.Value = TextBox1.Text;
    param = cmd.Parameters.Add("@password", SqlDbType.VarChar, 20);
    param.Value = BitConverter.ToString(hashbytes);
    try
    {
        con.Open();
        cmd.ExecuteNonQuery();
        Label4.Text = "Successfully added account!!!";
    }
    catch (Exception ex)
    {
        throw new Exception("Exception adding account"+ex.Message);
    }
    finally
        {
            con.Close();
        }
}

How to compare this password with the one coming from login page... Help me out guys... 如何将此密码与登录页面上的密码进行比较...帮帮我...

Code for HASH with SALT:- 带SALT的HASH代码:-

 private static string CreateSalt(int size)

{

    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

    byte[] buff = new byte[size];

    rng.GetBytes(buff);

    return Convert.ToBase64String(buff);

}

private static string CreatePasswordHash(string pwd, string salt)

{

    string saltAndPwd = String.Concat(pwd, salt);

    string hashedPwd =

    FormsAuthentication.HashPasswordForStoringInConfigFile(saltAndPwd, "SHA1");

    hashedPwd = String.Concat(hashedPwd, salt);

    return hashedPwd;

}

protected void btnregister_Click(object sender, EventArgs e)

{

    int saltSize = 5;

    string salt = CreateSalt(saltSize);

    string passwordHash = CreatePasswordHash(txtPassword.Text, salt);

    try

    {

        StoreAccountDetails(txtUserName.Text, passwordHash);

    }

    catch (Exception ex)

    {

        lblMessage.Text = ex.Message;

    }

}

private void StoreAccountDetails( string userName,string passwordHash )

{

    SqlConnection conn = new SqlConnection(constr);

    SqlCommand cmd = new SqlCommand("INSERT INTO Users VALUES(@userName, @passwordHash)", conn);

    SqlParameter sqlParam = null;

    sqlParam = cmd.Parameters.Add("@userName", SqlDbType.VarChar,20);

    sqlParam.Value = userName;

    sqlParam = cmd.Parameters.Add("@passwordHash ", SqlDbType.VarChar,50);

    sqlParam.Value = passwordHash;

    try

    {

       conn.Open();

       cmd.ExecuteNonQuery();

       lblMessage.Text = "User Added Successfully!!!";

    }

    catch( Exception ex )

    {

    throw new Exception("Exception adding account. " + ex.Message);

    }

    finally

    {

       conn.Close();

    }

}

private bool VerifyPassword(string suppliedUserName,string suppliedPassword )

{

     bool passwordMatch=false;

     SqlConnection conn = new SqlConnection(constr);

     SqlCommand cmd = new SqlCommand( "SELECT PasswordHash FROM Users WHERE UserName = @userName", conn );

     SqlParameter sqlParam = cmd.Parameters.Add("@userName",SqlDbType.VarChar,20);

     sqlParam.Value = suppliedUserName;

     try

     {

        conn.Open();

        SqlDataReader reader = cmd.ExecuteReader();

        reader.Read();

        string dbPasswordHash = reader.GetString(0);

        int saltSize = 5;

        string salt = CreateSalt(saltSize);

        reader.Close();

        string hashedPasswordAndSalt =CreatePasswordHash(suppliedPassword, salt);

        passwordMatch = hashedPasswordAndSalt.Equals(dbPasswordHash);

     }

     catch (Exception ex)

     {

        throw new Exception("Execption verifying password. " +ex.Message);

     }

     finally

     {

        conn.Close();

     }

    return passwordMatch;
 }


protected void btnlogon_Click(object sender, EventArgs e)

{

    bool passwordVerified=false;

    try

    {

        passwordVerified =VerifyPassword(txtUserName.Text, txtPassword.Text);

    }

    catch (Exception ex)

    {

        lblMessage.Text = ex.Message;

        return;

    }

    if (passwordVerified == true)

    {



        lblMessage.Text = "Logon successful: User is authenticated";

    }

    else

    {

        lblMessage.Text = "Invalid username or password";

    }

}

Simple. 简单。 You hash the password coming from the login page, and compare the hash code to the hash code in the database. 您对来自登录页面的密码进行哈希处理,然后将哈希码与数据库中的哈希码进行比较。


As the text should be handled the same both times, make a single function for that part. 由于文本应两次相同地处理,因此请对该部分做一个单一的功能。 Use UTF-8 when converting the text to bytes, the ASCII encoding doesn't handle all characters: 将文本转换为字节时使用UTF-8,ASCII编码不能处理所有字符:

public static string HashString(string value) {
  SHA384CryptoServiceProvider sh = new SHA384CryptoServiceProvider();
  byte[] plainbytes = Encoding.UTF8.GetBytes(value);
  byte[] hash = sh.ComputeHash(plainbytes);
  return BitConverter.ToString(hashbytes);
}

Update 更新资料

Now that you updated your code I still see a few problems. 现在您更新了代码,我仍然看到一些问题。 You should use a Salt, but first you need your code to actually work. 您应该使用Salt,但首先需要您的代码才能实际工作。 Adding a salt is easy. 加盐很容易。

A simple SHA hash of "mypassword" results in a string much larger than 20 characters. 一个简单的SHA哈希值“ mypassword”会导致字符串大于20个字符。 There is no way you can fit the base64'd hashed password in a 20 character field. 您无法在20个字符的字段中放入base64哈希密码。 Change the column size and your code to support larger hashes. 更改列大小和代码以支持更大的哈希。

Update 2 更新2

Here is the code with the Salt on the hash. 这是在哈希表上加上Salt的代码。 You have to update your RegisterUser stored procedure to store the salt along with the username and hashed password. 您必须更新RegisterUser存储过程以将盐与用户名和哈希密码一起存储。 You need a new Salt per user. 每位用户需要一个新的Salt。 The salt itself does not need to be hashed or encrypted. 盐本身不需要进行哈希处理或加密。

You also need to return the salt from your LogInUser stored procedure. 您还需要从LogInUser存储过程中返回盐。

Read comments below: 阅读下面的评论:

private byte[] Combine(byte[] a, byte[] b)
{
   byte[] c = new byte[a.Length + b.Length]; 
   System.Buffer.BlockCopy(a, 0, c, 0, a.Length); 
   System.Buffer.BlockCopy(b, 0, c, a.Length, b.Length); 
   return c; 
}

protected void Button1_Click(object sender, EventArgs e)
{    
    byte[] salt = new byte[16];
    RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
    random.GetNonZeroBytes(salt);

    SHA384CryptoServiceProvider sh = new SHA384CryptoServiceProvider();
    byte[] plainbytes = Encoding.ASCII.GetBytes(TextBox2.Text);

    var saltedBytes = Combine (salt, plainbytes);
    var sha = sh.ComputeHash(saltedBytes);

    SqlConnection con = new SqlConnection(constr);
    SqlCommand cmd = new SqlCommand("RegisterUser",con);
    cmd.CommandType = CommandType.StoredProcedure;

    SqlParameter param = null;
    param = cmd.Parameters.Add("@username",SqlDbType.VarChar,10);
    param.Value = TextBox1.Text;

    param = cmd.Parameters.Add("@password", SqlDbType.VarChar, 128);
    param.Value = Convert.ToBase64String(sha);

    // Store salt to use when comparing.
    param = cmd.Parameters.Add("@salt", SqlDbType.VarChar, 128);
    param.Value = Convert.ToBase64String(salt);

    try
    {
        con.Open();
        cmd.ExecuteNonQuery();
        Label4.Text = "Successfully added account!!!";
    }
    catch (Exception ex)
    {
        throw new Exception("Exception adding account"+ex.Message);
    }
    finally
    {
        con.Close();
    }
}

public bool searchtable() 
{           
    SqlConnection con = new SqlConnection(constr);
    SqlCommand cmd = new SqlCommand("LogInUser",con);
    cmd.CommandType = CommandType.StoredProcedure;

    SqlParameter param = null;
    param = cmd.Parameters.Add("@username",SqlDbType.VarChar,10);
    param.Value = TextBox1.Text;

    try
    {
        con.Open();
        SqlDataReader reader = cmd.ExecuteReader();
        reader.Read();

        // Get the salt hashed password
        string dbpassmatch = reader.GetString(0);

        // Get the salt
        byte[] salt = Convert.FromBase64String(reader.GetString(1));

        // Recreate the salted hashed password
        byte[] plainbyte = Encoding.ASCII.GetBytes(TextBox2.Text);
        SHA384CryptoServiceProvider sh = new SHA384CryptoServiceProvider();
        var saltedBytes = Combine (salt, plainbytes);
        var sha = sh.ComputeHash(saltedBytes);            

        // Now it matches what you did in insert.
        String dbpassword = Convert.ToBase64String(sha);

        reader.Close();

        return dbpassword.Equals(dbpassmatch);
    }
    catch (Exception ex)
    {
        throw new Exception("Exception adding account" + ex.Message);
    }
    finally
    {
        con.Close();
    }
}

Your code is pretty bad from a security point of view: 从安全角度来看,您的代码非常糟糕:

  1. Use a salt. 用盐。 Typically a random value(64 bit or more) stored alongside the hash in the db. 通常,随机值(64位或更多)与哈希一起存储在数据库中。
  2. Use a slow KDF, such as PBKDF2, bcrypt or scrypt 使用慢速KDF,例如PBKDF2,bcrypt或scrypt
    PBKDF2 is easiest, since .net already contains an implementation: Rfc2898DeriveBytes Class PBKDF2最简单,因为.net已经包含一个实现: Rfc2898DeriveBytes类
  3. Don't use Encoding.ASCII , but Encoding.UTF8 . 不要使用Encoding.ASCII ,而要使用Encoding.UTF8 Else passwords containing non ASCII characters become very weak. 包含非ASCII字符的其他密码会变得很弱。

Then to verify an entered password, read the hash and salt from the db. 然后,要验证输入的密码,请从数据库中读取哈希和盐。 Hash the candidate password with the salt you read from the db, and compare the hashes. 将候选密码与您从数据库读取的盐一起哈希,然后比较哈希值。


ASP.net also contains a library called Membership , but I'm not too familiar with it. ASP.net还包含一个名为Membership的库,但我对此不太熟悉。

you could do the hashing in the db side, not the application side. 您可以在db端而不是应用程序端进行哈希处理。 that would be the easiest, youll send the password as plain text to the register stored proceudre, it will hash it (with something like sha1) then you will send the password again when the user performs a login, the stored procedure will look for the username you have sent, then will hash the password and compare it to the password saved during registration. 这将是最简单的操作,您将以纯文本形式将密码发送到存储过程的寄存器,它将对其进行哈希处理(类似于sha1),然后在用户执行登录时再次发送密码,存储过程将查找您发送的用户名,然后将哈希密码并将其与注册期间保存的密码进行比较。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM