我使用c#设计GUI的登录窗口,用户首先提供用户名和密码,程序将其与MySQL数据库中的预定义用户列表进行匹配。如果正确,则允许用户打开GUI。
我发现MySQL中的“PBKDF2-SHA256”算法对密码进行了哈希处理。因此,对于用户名和密码都等于test
的用户,通过调试,我发现MySQL的哈希密码是这样的:
pbkdf2_sha256$12000$d8D3H2ZczGuG$H/U3ioBaeUeYPCP2YqLwpkjI40PoMHqeJyPORz8prUg=
我知道它使用pbkdf2_sha256
来对密码进行哈希处理,并在d8D3H2ZczGuG
迭代时将其加密,最终结果为12000
。因此,在验证密码时,我希望我的程序首先使用相同的算法和参数对输入密码进行哈希处理,如果获得的最终结果与H/U3ioBaeUeYPCP2YqLwpkjI40PoMHqeJyPORz8prUg=
相同,则表示密码正确。对于H/U3ioBaeUeYPCP2YqLwpkjI40PoMHqeJyPORz8prUg=
的c#实现,我直接使用this一个。
我的代码如下:
(输入用户名的文本框命名为“TextBox_Username”,而密码框命名为“PasswordBox_Password”):
pbkdf2_sha256 algorithm
但现在的问题是,我的程序对同一密码的散列输出与MySQL数据库不同。例如,对于密码为“test”,我的程序给出了这个输出(在将输出字节转换为字符串之后):
string server = "127.0.0.1";
string database = "myDatabase";
string uid = "supervisor";
string password = "12345";
string connectionString = "server=" + server + ";" + "database="+database + ";" + "uid=" + uid ";" + "password=" + password + ";";
MySqlConnection sqlConnection = new MySqlConnection(connectionString);
MySqlCommand cmd = new MySqlCommand();
cmd.CommandText = "SELECT Password FROM myTable_Usertable WHERE Username='" + this.TextBox_UserName.Text + "';";
sqlConnection.Open();
cmd.Connection = sqlConnection;
MySqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
string dbUserPassword= reader[0].ToString();
string[] passwordString = dbUserPassword.Split("$");
int iterationCount = Convert.ToInt32(passwordString[1]);
string saltString = passwordString[2];
string dbPasswordString = passwordString[3];
int dkLength = 44;
byte[] inputPasswordByte = System.Text.Encoding.UTF8.GetBytes(this.PasswordBox_Password.Password);
byte[] saltByte = System.Text.Encoding.UTF8.GetBytes(saltString);
byte[] hashedPasswordByte = PBKDF2Sha256GetBytes(dkLength,inputPasswordByte,saltByte,iterationCount);
byte[] dbPasswordByte = System.Text.Encoding.UTF8.GetBytes(dbPasswordString);
if( hashedPasswordByte == dbPasswordByte)
{
MessageBox.Show("Login succeed!");
MainWindow.show();
}
else
{
MessageBox.Show("Invalid password!");
}
}
// this following function is from the above link
private static byte[] PBKDF2Sha256GetBytes(int dklen, byte[] password, byte[] salt, int iterationCount)
{
using (var hmac = new System.Security.Cryptography.HMACSHA256(password))
{
int hashLength = hmac.HashSize / 8;
if ((hmac.HashSize & 7) != 0)
hashLength++;
int keyLength = dklen / hashLength;
if ((long)dklen > (0xFFFFFFFFL * hashLength) || dklen < 0)
throw new ArgumentOutOfRangeException("dklen");
if (dklen % hashLength != 0)
keyLength++;
byte[] extendedkey = new byte[salt.Length + 4];
Buffer.BlockCopy(salt, 0, extendedkey, 0, salt.Length);
using (var ms = new System.IO.MemoryStream())
{
for (int i = 0; i < keyLength; i++)
{
extendedkey[salt.Length] = (byte)(((i + 1) >> 24) & 0xFF);
extendedkey[salt.Length + 1] = (byte)(((i + 1) >> 16) & 0xFF);
extendedkey[salt.Length + 2] = (byte)(((i + 1) >> 8) & 0xFF);
extendedkey[salt.Length + 3] = (byte)(((i + 1)) & 0xFF);
byte[] u = hmac.ComputeHash(extendedkey);
Array.Clear(extendedkey, salt.Length, 4);
byte[] f = u;
for (int j = 1; j < iterationCount; j++)
{
u = hmac.ComputeHash(u);
for (int k = 0; k < f.Length; k++)
{
f[k] ^= u[k];
}
}
ms.Write(f, 0, f.Length);
Array.Clear(u, 0, u.Length);
Array.Clear(f, 0, f.Length);
}
byte[] dk = new byte[dklen];
ms.Position = 0;
ms.Read(dk, 0, dklen);
ms.Position = 0;
for (long i = 0; i < ms.Length; i++)
{
ms.WriteByte(0);
}
Array.Clear(extendedkey, 0, extendedkey.Length);
return dk;
}
}
}
虽然MySQL输出就是这个(如上所述):
?7??ZyG?<#?b??H??C?0z?'#?G?)?Ht\r5_[?% y
所以它说即使是正确的密码,提供的密码也是错误的。我猜测我的程序输出可能是十六进制的,所以我试着用MySQL字符串将我的最终结果转换为十六进制:
H/U3ioBaeUeYPCP2YqLwpkjI40PoMHqeJyPORz8prUg=
结果是这个(再次不一样):
string hexDbPassword = BitConverter.ToString(dbPasswordByte);
string hexHashedPassword = BitConverter.ToString(hashedPasswordByte);
我在这里做错了什么?