C# 如何将用户提供的登录凭据与数据库中存储的salt和hash进行比较以允许用户登录

C# 如何将用户提供的登录凭据与数据库中存储的salt和hash进行比较以允许用户登录,c#,asp.net,hash,cryptography,salt,C#,Asp.net,Hash,Cryptography,Salt,我在网上做了一些研究,观看了一些YouTube视频,拼凑了一段代码,对密码进行加密,然后对加密后的密码进行散列,并将加密后的密码和散列存储到数据库中。 现在,我正在研究如何将用户提供的密码与数据库中存储的salt进行比较 我不知道从这里到哪里去,我真的需要一些指导和帮助。 虽然我尝试过手动获取salt并将其与verifyPassword进行比较。但它不起作用。我相信我对代码缺乏一些理解 用户注册页面 public class HashSalt { public strin

我在网上做了一些研究,观看了一些YouTube视频,拼凑了一段代码,对密码进行加密,然后对加密后的密码进行散列,并将加密后的密码和散列存储到数据库中。 现在,我正在研究如何将用户提供的密码与数据库中存储的salt进行比较

我不知道从这里到哪里去,我真的需要一些指导和帮助。 虽然我尝试过手动获取salt并将其与verifyPassword进行比较。但它不起作用。我相信我对代码缺乏一些理解

用户注册页面

public class HashSalt
    {
        public string Hash { get; set; }
        public string Salt { get; set; }
    }

    public static HashSalt GenerateSaltedHash(int size, string password)
    {
        var saltBytes = new byte[size];
        var provider = new RNGCryptoServiceProvider();
        provider.GetNonZeroBytes(saltBytes);
        var salt = Convert.ToBase64String(saltBytes);

        var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, saltBytes, 10000);
        var hashPassword = Convert.ToBase64String(rfc2898DeriveBytes.GetBytes(256));

        HashSalt hashSalt = new HashSalt { Hash = hashPassword, Salt = salt };
        return hashSalt;
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        HashSalt hashSalt = GenerateSaltedHash(64, txtPassConfirm.Text);
        try
        {
            SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["DataBase"].ConnectionString);
            con.Open();
            SqlCommand cmd = new SqlCommand("insert into Customer values (@FirstName, @LastName, @Nationality, @Email, @Tel);" +
                "insert into Account values(@Email2, @Salt, @Hash);", con);
            cmd.Parameters.AddWithValue("FirstName", txtFName.Text);
            cmd.Parameters.AddWithValue("LastName", txtLName.Text);
            cmd.Parameters.AddWithValue("Nationality", ddlNationality.SelectedItem.Text);
            cmd.Parameters.AddWithValue("Email", txtMail.Text);
            cmd.Parameters.AddWithValue("Tel", txtTel.Text);
            cmd.Parameters.AddWithValue("Email2", txtMail.Text);
            cmd.Parameters.AddWithValue("@salt", hashSalt.Salt);
            cmd.Parameters.AddWithValue("@hash", hashSalt.Hash);

            cmd.ExecuteNonQuery();
            con.Close();
        }
        catch (SqlException ex)
        {
            if (ex.Number == 2627)
            {

            }
            else
            {
                throw;
            }
        }


        txtFName.Text = "";
        txtLName.Text = "";
        ddlNationality.SelectedItem.Text = "";
        txtTel.Text = "";
        txtMail.Text = "";

        Response.Redirect("Login.aspx");
    }
public static bool VerifyPassword(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;
    }

    protected void btnLogin_Click1(object sender, EventArgs e)
    {
        string ConnectionString = ConfigurationManager.ConnectionStrings["DataBase"].ConnectionString;

        SqlConnection myConnect = new SqlConnection(ConnectionString);

        string CommandText = "SELECT FirstName from Customer" + "SELECT Email, Salt, Hash from Account";
        SqlCommand cmd = new SqlCommand(CommandText, myConnect);
        myConnect.Open();
        SqlDataReader reader = cmd.ExecuteReader();


        while (reader.Read())
        {
            if (reader["Email"].ToString() == txtMail.Text )
            {
                bool isPasswordMatched = VerifyPassword(txtPass.Text,)
                Session["User"] = reader["FirstName"].ToString();

            }
            else
            {
                lblResult.Text = "Incorrect Username or Password, Please Try Again!";
            }
        }
    }
用户登录页面

public class HashSalt
    {
        public string Hash { get; set; }
        public string Salt { get; set; }
    }

    public static HashSalt GenerateSaltedHash(int size, string password)
    {
        var saltBytes = new byte[size];
        var provider = new RNGCryptoServiceProvider();
        provider.GetNonZeroBytes(saltBytes);
        var salt = Convert.ToBase64String(saltBytes);

        var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, saltBytes, 10000);
        var hashPassword = Convert.ToBase64String(rfc2898DeriveBytes.GetBytes(256));

        HashSalt hashSalt = new HashSalt { Hash = hashPassword, Salt = salt };
        return hashSalt;
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        HashSalt hashSalt = GenerateSaltedHash(64, txtPassConfirm.Text);
        try
        {
            SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["DataBase"].ConnectionString);
            con.Open();
            SqlCommand cmd = new SqlCommand("insert into Customer values (@FirstName, @LastName, @Nationality, @Email, @Tel);" +
                "insert into Account values(@Email2, @Salt, @Hash);", con);
            cmd.Parameters.AddWithValue("FirstName", txtFName.Text);
            cmd.Parameters.AddWithValue("LastName", txtLName.Text);
            cmd.Parameters.AddWithValue("Nationality", ddlNationality.SelectedItem.Text);
            cmd.Parameters.AddWithValue("Email", txtMail.Text);
            cmd.Parameters.AddWithValue("Tel", txtTel.Text);
            cmd.Parameters.AddWithValue("Email2", txtMail.Text);
            cmd.Parameters.AddWithValue("@salt", hashSalt.Salt);
            cmd.Parameters.AddWithValue("@hash", hashSalt.Hash);

            cmd.ExecuteNonQuery();
            con.Close();
        }
        catch (SqlException ex)
        {
            if (ex.Number == 2627)
            {

            }
            else
            {
                throw;
            }
        }


        txtFName.Text = "";
        txtLName.Text = "";
        ddlNationality.SelectedItem.Text = "";
        txtTel.Text = "";
        txtMail.Text = "";

        Response.Redirect("Login.aspx");
    }
public static bool VerifyPassword(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;
    }

    protected void btnLogin_Click1(object sender, EventArgs e)
    {
        string ConnectionString = ConfigurationManager.ConnectionStrings["DataBase"].ConnectionString;

        SqlConnection myConnect = new SqlConnection(ConnectionString);

        string CommandText = "SELECT FirstName from Customer" + "SELECT Email, Salt, Hash from Account";
        SqlCommand cmd = new SqlCommand(CommandText, myConnect);
        myConnect.Open();
        SqlDataReader reader = cmd.ExecuteReader();


        while (reader.Read())
        {
            if (reader["Email"].ToString() == txtMail.Text )
            {
                bool isPasswordMatched = VerifyPassword(txtPass.Text,)
                Session["User"] = reader["FirstName"].ToString();

            }
            else
            {
                lblResult.Text = "Incorrect Username or Password, Please Try Again!";
            }
        }
    }
数据库表

CREATE TABLE [dbo].[Account] (
    [EMail] NVARCHAR (50)  NOT NULL,
    [Salt]  NVARCHAR (MAX) NOT NULL,
    [Hash]  NVARCHAR (MAX) NOT NULL,
    PRIMARY KEY CLUSTERED ([EMail] ASC)
);

如果您愿意在散列存储中使用Salt,那么应该将密码输入的Salt值保留在数据库中,附加到散列值中,或者像这样存储在另一列中

要验证密码,您的代码需要查询给定用户的salt值,并在哈希摘要方法中使用该值,方法与您在密码salt+哈希计算中使用的方法相同,然后进行比较

总结如下:

  • 为新密码存储请求生成随机salt值
  • 定义要附加盐值的位置。您可以进行连接,例如salt+password、password+salt。重要的是生成包含salt和密码的hash摘要作为输入,以增加rainbow表的使用难度
  • 存储用密码和salt生成的哈希,并存储salt
  • 当用户尝试登录时,在数据库中获取该用户的salt值,然后根据他的输入应用相同的哈希方法,将其与查询的salt连接起来,与哈希存储方法相同
  • 如果两个哈希摘要相等,则可以继续进行身份验证

  • 看看这个,更好地理解Salt。

    如果您愿意在散列存储中使用Salt,您应该将密码输入的Salt值保留在数据库中,附加到散列值后,或者像这样存储在另一列中

    要验证密码,您的代码需要查询给定用户的salt值,并在哈希摘要方法中使用该值,方法与您在密码salt+哈希计算中使用的方法相同,然后进行比较

    总结如下:

  • 为新密码存储请求生成随机salt值
  • 定义要附加盐值的位置。您可以进行连接,例如salt+password、password+salt。重要的是生成包含salt和密码的hash摘要作为输入,以增加rainbow表的使用难度
  • 存储用密码和salt生成的哈希,并存储salt
  • 当用户尝试登录时,在数据库中获取该用户的salt值,然后根据他的输入应用相同的哈希方法,将其与查询的salt连接起来,与哈希存储方法相同
  • 如果两个哈希摘要相等,则可以继续进行身份验证

  • 请看一看,以便更好地理解盐析。

    您可以尝试以下方法来创建和检索哈希:

            public static byte[] GetHash(string inputString)
            {
                HashAlgorithm algorithm = SHA256.Create();
                return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
            }
    
            public static string GetHashString(string inputString)
            {
                StringBuilder sb = new StringBuilder();
                foreach (byte b in GetHash(inputString))
                    sb.Append(b.ToString("X2"));
    
                return sb.ToString();
            }
    
    
    然后,当用户注册时,获取明文密码并生成随机salt,您只需像这样保存密码哈希:

          newUser.password = GetHashString(salt + password);
    
    别忘了还将salt保存在数据库中的某个位置(我通常将salt保存在与密码哈希相同的表中)

    现在,当用户想要登录时,获取他们的输入并在数据库中搜索输入的用户名,如果返回结果(假设用户名必须是唯一的并且他们输入了有效的用户名,则应该只有一个结果),然后获取与该帐户相关联的salt并再次生成哈希:

          string compareHash = GetHashString(salt + password);
    

    现在只需将计算出的哈希值与数据库中存储的哈希值进行比较,如果两者匹配,则登录它们

    您可以尝试此方法创建和检索哈希:

            public static byte[] GetHash(string inputString)
            {
                HashAlgorithm algorithm = SHA256.Create();
                return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
            }
    
            public static string GetHashString(string inputString)
            {
                StringBuilder sb = new StringBuilder();
                foreach (byte b in GetHash(inputString))
                    sb.Append(b.ToString("X2"));
    
                return sb.ToString();
            }
    
    
    然后,当用户注册时,获取明文密码并生成随机salt,您只需像这样保存密码哈希:

          newUser.password = GetHashString(salt + password);
    
    别忘了还将salt保存在数据库中的某个位置(我通常将salt保存在与密码哈希相同的表中)

    现在,当用户想要登录时,获取他们的输入并在数据库中搜索输入的用户名,如果返回结果(假设用户名必须是唯一的并且他们输入了有效的用户名,则应该只有一个结果),然后获取与该帐户相关联的salt并再次生成哈希:

          string compareHash = GetHashString(salt + password);
    

    现在只需将计算出的哈希值与数据库中存储的哈希值进行比较,如果两者匹配,则登录它们

    您似乎编写了正确的验证例程(除了不使用固定时间的相等性比较)。你真正的问题是什么/你被困在哪里?@bartonjs,我被困在调用方法上。我的引用是使用user user=getuserdetails这类代码,然后在verifypassword方法中调用它来比较salt和hash。这是我不明白的部分。此外,您能否详细说明固定时间平等比较?是否包含它是关键和重要的?您似乎已经编写了正确的验证例程(除了不使用固定时间的相等性比较)。你真正的问题是什么/你被困在哪里?@bartonjs,我被困在调用方法上。我的引用是使用user user=getuserdetails这类代码,然后在verifypassword方法中调用它来比较salt和hash。这是我不明白的部分。进一步