C# Hash Algorithm is not working correctly - c#

I have passwordHash and passwordSalt in my user model and database for register. I am posting email and password to my API. I am getting user by email from database, and I am hashing coming password from API. Furthermore, I compare to each other, but it isn't working correctly
I worked like that a lot of times, but I have never lived trouble, I upgraded my operating system to Windows 11. I don't know, maybe it is reason Windows 11.
public class HashingHelper
{
public static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
{
using (var hmac = new System.Security.Cryptography.HMACSHA512())
{
passwordSalt = hmac.Key;
passwordHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password));
}
}
public static bool VerifyPasswordHash(string password, byte[] passwordHash, byte[] passwordSalt)
{
using (var hmac = new System.Security.Cryptography.HMACSHA512(passwordSalt))
{
var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password));
for (int i = 0; i < computedHash.Length; i++)
{
if (computedHash[i] != passwordHash[i])
{
return false;
}
}
return true;
}
}
}
CreatePasswordHash method is hashing password before register.
VerifyPasswordHash method is comparing coming password from API and coming password from my database

Related

Changing Storage of Password to Storing of Hashes in C# Forms

My current project is using Microsoft's Membership to manage user's credentials.
For now, it stores passwords into SQL DB in plain-text.
I want to change it store hashes instead, or to encrypt/hide password column.
Current code:
protected void BtnSubmit_Click(object sender, EventArgs e)
{
Page.Validate();
if (Page.IsValid)
{
NAME_OF_TABLE_Provider provider = new NAME_OF_TABLE_Provider();
string userName = SPContext.Current.Web.CurrentUser.Name;
MembershipUserCollection userMembership = Membership.GetAllUsers();
MembershipUser membershipUser = userMembership[userName];
if (membershipUser != null)
{
try
{
membershipUser.ChangePassword(OldPassword.Text, NewPassword.Text);
ConfirmPanel.Visible = true;
InvalidPanel.Visible = false;
Message.InnerText = "The password is changed successfully";
}
catch (Exception ex)
{
ConfirmPanel.Visible = false;
InvalidPanel.Visible = true;
InvalidPassword.InnerText = "The password is not strong. Please verify.";
}
}
}
}
#1 Possible(?) Solution: HashBytes
INSERT INTO <tbl> (..., passwd) values (...., HashBytes('SHA1', #password))
SELECT HashBytes('SHA1', #password);
#2 Possible(?) Solution: C# Storing of Hashes
static void Main(string[] args)
{
//Store a password hash:
PasswordHash hash = new PasswordHash("password");
byte[] hashBytes = hash.ToArray();
//For testing purposes
Console.WriteLine(Convert.ToBase64String(hashBytes));
//Check password against a stored hash
byte[] hashBytes2 = hashBytes;//read from store.
PasswordHash hash2 = new PasswordHash(hashBytes2);
if (!hash.Verify("password"))
{
throw new System.UnauthorizedAccessException();
}
else
{
Console.WriteLine("True");
}
Console.ReadLine();
}
}
public sealed class PasswordHash
{
const int SaltSize = 16, HashSize = 20, HashIter = 10000;
readonly byte[] _salt, _hash;
public PasswordHash(string password)
{
new RNGCryptoServiceProvider().GetBytes(_salt = new byte[SaltSize]);
_hash = new Rfc2898DeriveBytes(password, _salt, HashIter).GetBytes(HashSize);
}
public PasswordHash(byte[] hashBytes)
{
Array.Copy(hashBytes, 0, _salt = new byte[SaltSize], 0, SaltSize);
Array.Copy(hashBytes, SaltSize, _hash = new byte[HashSize], 0, HashSize);
}
public PasswordHash(byte[] salt, byte[] hash)
{
Array.Copy(salt, 0, _salt = new byte[SaltSize], 0, SaltSize);
Array.Copy(hash, 0, _hash = new byte[HashSize], 0, HashSize);
}
public byte[] ToArray()
{
byte[] hashBytes = new byte[SaltSize + HashSize];
Array.Copy(_salt, 0, hashBytes, 0, SaltSize);
Array.Copy(_hash, 0, hashBytes, SaltSize, HashSize);
return hashBytes;
}
public byte[] Salt { get { return (byte[])_salt.Clone(); } }
public byte[] Hash { get { return (byte[])_hash.Clone(); } }
public bool Verify(string password)
{
byte[] test = new Rfc2898DeriveBytes(password, _salt, HashIter).GetBytes(HashSize);
for (int i = 0; i < HashSize; i++)
if (test[i] != _hash[i])
return false;
return true;
}
#3 Possible(?) Solution: Transact-SQL to encrypt single column
https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/encrypt-a-column-of-data?view=sql-server-2017#TsqlProcedure
May I know what is the correct approach? Thank you.
It goes without saying that any solution is better than the current one.
The first solution looks solid, but I could not find out if a salt is used. If not, that's a disadvantage. You also are now dependent on this database vendor and cannot switch.
The second solution looks good, but I didn't look too closely, you should probably head over to CodeReview, they do reviews of working code to look for improvements.
The last solution is not really a solution. Passwords are hashed not exncrypted for a reason. If you can decrypt them, the attacker who gets access to the system and steals them will have the same access to the decryption means as you do. So it's a layer of inconvenience, not security.
So pick number two and let somebody look over it for weaknesses or bugs.

Issue With Encrypted Password in C# using Rfc2898DeriveBytes and MSSQL

I have made a login system for my application, however, it is working extremely inconsistently.
Sometimes the password will work, but other times it says it's incorrect. I'm 100% sure I'm typing it correctly.
To store the passwords, I generate a random salt, hash the password with the salt and store the hashed password and salt along with the username in the database.
To authenticate the user, I select the hashed password and salt based on the given username. I then hash their password attempt with the salt and see if it matches their original hashed password, allowing them to log in if so.
My code is as follows:
private const int NumberOfRounds = 5000, SaltLength = 32;
public static byte[] GenerateSalt()
{
using (var rng = new RNGCryptoServiceProvider())
{
var randomNumber = new byte[SaltLength];
rng.GetBytes(randomNumber);
return randomNumber;
}
}
public static byte[] HashPassword(byte[] passwordToHash, byte[] salt)
{
using (var deriveBytes = new Rfc2898DeriveBytes(passwordToHash, salt, numberOfRounds))
{
return deriveBytes.GetBytes(32);
}
}
public static bool IsPasswordValid(string inputPassword, string hashedPassword, string salt)
{
byte[] potentialValidPassword = HashPassword(Encoding.Unicode.GetBytes(inputPassword),
Encoding.Unicode.GetBytes(salt));
string potentialAsString = Encoding.Unicode.GetString(potentialValidPassword);
return Encoding.Unicode.GetBytes(hashedPassword).SequenceEqual(potentialValidPassword) ||
hashedPassword.Equals(potentialAsString);
}
The reason my check compares both the byte array value and the string value is that sometimes the byte value comparison fails but the string value works.
My code to insert a user into the database is as follows
public SecurityReturnMessage AddUser(string username, string password)
{
byte[] salt = PasswordManagement.GenerateSalt();
byte[] hashedPassword = PasswordManagement.HashPassword(Encoding.Unicode.GetBytes(password), salt);
if (conn.State != ConnectionState.Open)
conn.Open();
int result;
using (IDbCommand comm = conn.CreateCommand())
{
comm.CommandText = "usp_IFRS_SEC_USER_INSERT";
comm.CommandType = CommandType.StoredProcedure;
SqlParameter hashPwd =new SqlParameter("#hashpwd", SqlDbType.NVarChar)
{
Value = Encoding.Unicode.GetString(hashedPassword)
};
SqlParameter saltParameter = new SqlParameter("#salt", SqlDbType.NVarChar)
{
Value = Encoding.Unicode.GetString(salt)
};
comm.Parameters.Add(Extensions.CreateParameter(comm, "#user", username));
comm.Parameters.Add(hashPwd);
comm.Parameters.Add(saltParameter);
var returnVal = comm.CreateParameter();
returnVal.Direction = ParameterDirection.ReturnValue;
comm.Parameters.Add(returnVal);
comm.ExecuteNonQuery();
result = (int)returnVal.Value;
}
if (conn.State != ConnectionState.Closed)
conn.Close();
return (SecurityReturnMessage)result;
}
If anyone could help me out with this I'd be extremely grateful.
I Suggest you to use the Base64 conversions as below when you convert values from Bytes to String and vice versa.
string base64 = Convert.ToBase64String(bytes);
byte[] bytes = Convert.FromBase64String(base64);
This way you can make sure that the unicode data is not lost or corrupted during either of the conversion process, which might be a valid reason for your code's inconsistency.

Password hashing in UWP

I have the following code in .net framework.
public string GetHashedPassword(string password, string salt)
{
byte[] saltArray = Convert.FromBase64String(salt);
byte[] passArray = Convert.FromBase64String(password);
byte[] salted = new byte[saltArray.Length + passArray.Length];
byte[] hashed = null;
saltArray.CopyTo(salted, 0);
passArray.CopyTo(salted, saltArray.Length);
using (var hash = new SHA256Managed())
{
hashed = hash.ComputeHash(salted);
}
return Convert.ToBase64String(hashed);
}
I'm trying to create an equivalent in .net core for a UWP application. Here's what I have so far.
public string GetHashedPassword(string password, string salt)
{
IBuffer input = CryptographicBuffer.ConvertStringToBinary(password + salt, BinaryStringEncoding.Utf8);
var hashAlgorithm = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var hash = hashAlgorithm.HashData(input);
//return CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, hash);
}
The last line, converting the buffer back to a string doesn't work. I get this exception:
No mapping for the Unicode character exists in the target multi-byte code page.
How can I convert the buffer back into a string?
I am assuming, that you want to get the hashed password in a base64-format, because you did that in your .net example.
To get this, change:
CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, hash);
to:
CryptographicBuffer.EncodeToBase64String(hash);
So the complete method looks like this:
public string GetHashedPassword(string password, string salt)
{
IBuffer input = CryptographicBuffer.ConvertStringToBinary(password + salt, BinaryStringEncoding.Utf8);
var hashAlgorithm = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var hash = hashAlgorithm.HashData(input);
return CryptographicBuffer.EncodeToBase64String(hash);
}

Creating a function in .net for login

I have salt and encrypted password in my db. and want to create a login function. I created a login function but it always returns false.
My function for conversion is here, is it right or not?
public static String HashPassword(String password, String salt)
{
var combinedPassword = String.Concat(password, salt);
var sha256 = new SHA256Managed();
var bytes = UTF8Encoding.UTF8.GetBytes(combinedPassword);
var hash = sha256.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
public static Boolean ValidatePassword(String enteredPassword, String storedHash, String storedSalt)
{
// Consider this function as an internal function where parameters like
// storedHash and storedSalt are read from the database and then passed.
var hash = HashPassword(enteredPassword, storedSalt);
return String.Equals(storedHash, hash);
}

WCF/REST hash/salt datamember field

I have a datacontract and in my service I am trying to hash/salt the password datamember:
public void AddStudent(Student student)
{
student.StudentID = (++eCount).ToString();
byte[] passwordHash = Hash(student.Password, _passwordSalt); //invalid expression? _passwordSalt?
student.TimeAdded = DateTime.Now;
students.Add(student);
}
Can anyone help?
Try to replace the _passwordSalt with this function GenerateSalt() from one of my projects:
protected RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
public byte[] GenerateSalt() {
byte[] salt = new byte[10];
random.GetNonZeroBytes(salt);
return salt;
}
By the way you have to save this generated salt. You need the same salt every time to check the password.

Categories

Resources