如何在 c# 中验证 windows 应用程序的加盐密码和用户名?

Posted

技术标签:

【中文标题】如何在 c# 中验证 windows 应用程序的加盐密码和用户名?【英文标题】:How to validate Salted Password and Username for windows application in c#? 【发布时间】:2020-07-23 00:33:49 【问题描述】:

我有一个简单的登录窗口应用程序。我已经在本地主机中成功创建了一个 SQL 数据库,其中包含用户名、密码(哈希)、Salt 和电子邮件等用户信息,并且我还成功创建了一个用户注册表单,新用户可以在其中输入他们的详细信息,这些详细信息将被添加到数据库,它工作正常。

但我无法根据数据库(本地主机)中包含用户数据的行中存储的值验证用户名和密码以授予访问权限。

我已经能够编写散列、加盐和验证方法以供进一步使用。

HashSalt.cs 类

        // --- 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;
        







    

这是我的 登录表单的Login.cs

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

数据库结构 如果您能给我一些操作说明,我将不胜感激。

【问题讨论】:

你能显示你将哈希存储到数据库中的代码吗?您是否真的将哈希值存储在名为密码的列中? 不,它们在两个不同的列密码(哈希)和盐 所以生成用户的时候不存储hash? 一个小警告:如果这是你计划投入生产的东西,你真的需要知道你在这里做什么 - 很容易在你的自己的密码加密/验证实现。但这对你来说是一个很好的练习,如果你只是在玩:) 我建议寻找其他可以帮助你解决这个问题的库。解决您的问题:在generateHash() 中创建一个 32 字节的密钥,但在 AuthenticateUser() 中创建一个 256 字节的密钥。那些永远不会一样。此外,您似乎使用了不同数量的迭代。 @Xerillio,感谢您提及generateHash()AuthenticateUser() 之间的字节密钥差异 我之前没有看到,顺便说一句,我只是在玩,但根据根据我对互联网 PBKDF2、Scrypt、Bcrypt 的研究,只要正确的实现和良好的成本参数,所有这些功能都非常安全。here 【参考方案1】:

我认为问题在于 mysql 表中 salt 列的数据类型。 这里,

string newPass = hashSalt.generateHash(enteredPass, hashSalt.saltToByte(salt));

您将从数据库中获取的salt 传递给saltToByte() 方法以转换为 byte array,所以我认为您计划使用返回值generateSalt() 将盐存储为字符串 方法。当您需要对用户进行身份验证时,使用generateHash() 方法将其重新转换为byte array

这是一个混乱的实现。 建议您将盐存储为byte array,这样您就不需要使用saltToByte()

除此之外, generateSalt() 字节密钥设置为

var salt = new byte[32];

32 字节密钥,在 AuthenticateUser() 中,您使用的是 256 字节密钥 rfc2898DeriveBytes.GetBytes(256)

【讨论】:

以上是关于如何在 c# 中验证 windows 应用程序的加盐密码和用户名?的主要内容,如果未能解决你的问题,请参考以下文章

如何在C#中模拟没有密码的Windows身份验证登录用户

如何使用 c# 从 Windows 窗体向用户显示默认的 Windows 身份验证提示?

如何验证 Windows 服务是不是正在运行

我们如何使用 C# 在 SharePoint 中获取当前的身份验证模式

如何在 WPF C# 中更改验证结果的字体大小

如何在 C# 中获取当前用户的 Active Directory 详细信息