我正试图使用以下方法来实现安全密码存储 libsodium但 docsTestWorks
, hashesDontMatch
和 wrongPassword
似是 始终 是的 我觉得不应该是这样的。我是不是做错了什么(或者说很多事情)?
public async Task<IUser> Authenticate(ICredentials credentials)
{
using var connection = _databaseGateway.Connection;
connection.Open();
const string PASSWORD = "Correct Horse Battery Staple";
var hash = PasswordHash
.ScryptHashString(PASSWORD);
var docsTestWorks = PasswordHash.ScryptHashStringVerify(hash, PASSWORD);
var incomingHash = PasswordHash
.ScryptHashString(credentials.Password);
var test1 = PasswordHash
.ScryptHashString("myPassword");
var test2 = PasswordHash
.ScryptHashString("myPassword");
var hashesDontMatch = test1 != test2;
var users = await connection.GetAllAsync<User>();
var existingUser = users
.Single(u => u.Username == credentials.Username);
var wrongPassword = !PasswordHash
.ScryptHashStringVerify(existingUser.PasswordHash, credentials.Password);
if (wrongPassword)
throw new AuthenticationException(Error.WrongPassword,
"Error: Incorrect Password.");
return AddToken(existingUser);
}
我觉得这太简单了,我可能漏掉了很多东西......
我正在使用的软件包。
dotnet add package Sodium.Core
我写了一些使用 System.Security.Cryptography
命名空间来简化这种工作。
https:/github.comjscarleCrypto.NETblobmasterCrypto.NETSystem.Security.Cryptography.Extensions.cs。
一旦添加到您的项目中,请用以下方式引用命名空间 using System.Security.Cryptography
然后你可以从任何字符串中哈希和比较哈希值。
比如说
string password1 = "some password";
string hash1 = password1.Hash();
string password2 = "some other password";
string hash2 = password2.Hash();
string password3 = "some password";
string hash3 = password3.Hash();
Console.WriteLine($"{password1} = {hash1}");
Console.WriteLine($"{password2} = {hash2}");
Console.WriteLine($"{password3} = {hash3}");
if (password1.CompareToHash(hash2))
Console.WriteLine("hash1 and hash2 match.");
else
Console.WriteLine("hash1 and hash2 do not match.");
if (password1.CompareToHash(hash3))
Console.WriteLine("hash1 and hash3 match.");
else
Console.WriteLine("hash1 and hash3 do not match.");
我的结果是:
public async Task<IUser> Authenticate(ICredentials credentials)
{
using var connection = _databaseGateway.Connection;
connection.Open();
var users = await connection.GetAllAsync<User>();
const int expectedUserCount = 1;
var existingUsers = users
.Where(u => u.Username == credentials.Username);
if (existingUsers.Count() != expectedUserCount)
throw new AuthenticationException(Error.UserDoesNotExist,
"Error: There is no single user exists with the username given.");
var existingUser = existingUsers.Single();
var salt = existingUser.PasswordSalt;
// TURN ON FOR REGISTERING USER PW
// salt = PasswordHash.ScryptGenerateSalt();
var password = Encoding.UTF8.GetBytes(credentials.Password);
var hash = PasswordHash.ScryptHashBinary(password, salt);
// TURN ON FOR REGISTERING USER PW
// await connection.ExecuteAsync(
// "UPDATE user SET PasswordHash=@hash, PasswordSalt=@salt WHERE UserName=@username;",
// new {hash, salt, username = credentials.Username});
var wrongPassword = !hash.SequenceEqual(existingUser.PasswordHash);
if (wrongPassword)
throw new AuthenticationException(Error.WrongPassword,
"Error: Incorrect Password.");
return AddToken(existingUser);
}
怀疑 此处 是正确的,我想。它似乎每次都会随机生成盐与 string
方法的重载。
然后我最后添加了一个辅助类。
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sodium;
namespace Services
{
public static class HashingService
{
public static bool IsGeneratedBy(
this IEnumerable<byte> existingHash,
string plaintextPassword,
byte[] salt
)
{
var password = Encoding.UTF8.GetBytes(plaintextPassword);
var generatedHash = PasswordHash.ScryptHashBinary(password, salt);
var passwordIsCorrect = generatedHash.SequenceEqual(existingHash);
return passwordIsCorrect;
}
}
}
并像这样使用:
public async Task<IUser> Authenticate(ICredentials credentials)
{
using var connection = _databaseGateway.Connection;
connection.Open();
var users = await connection.GetAllAsync<User>();
const int expectedUserCount = 1;
var existingUser = users
.Single(u => u.Username == credentials.Username);
var wrongPassword = !existingUser.PasswordHash.IsGeneratedBy(
credentials.Password, existingUser.PasswordSalt);
if (wrongPassword)
throw new AuthenticationException(Error.WrongPassword,
"Error: Incorrect Password.");
return AddToken(existingUser);
}