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.
Related
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
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.
I use the following code to Encrypt and protect the password and I add salt to it, but when I try to validate it when the user login they don't match, I don't know why.
public static class Encrypt
{
public static string saltValue { get; set; }
public static string hashValue { get; set; }
public static void SecurePassword(string password)
{
// Create a truly random salt using RNGCryptoServiceProvider.
RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
byte[] salt = new byte[32];
csprng.GetBytes(salt);
// Get the salt value
saltValue = Convert.ToBase64String(salt);
// Salt the password
byte[] saltedPassword = Encoding.UTF8.GetBytes(saltValue + password);
// Hash the salted password using SHA256
SHA512Managed hashstring = new SHA512Managed();
byte[] hash = hashstring.ComputeHash(saltedPassword);
// Save both the salt and the hash in the user's database record.
saltValue = Convert.ToBase64String(salt);
hashValue = Convert.ToBase64String(hash);
}
public static void ValidateLogin(string password, string username)
{
// Read the user's salt value from the database
string saltValueFromDB = saltValue;
// Read the user's hash value from the database
string hashValueFromDB = hashValue;
byte[] saltedPassword = Encoding.UTF8.GetBytes(saltValueFromDB + password);
// Hash the salted password using SHA256
SHA512Managed hashstring = new SHA512Managed();
byte[] hash = hashstring.ComputeHash(saltedPassword);
string hashToCompare = Convert.ToBase64String(hash);
if (hashValueFromDB.Equals(hashToCompare))
Console.WriteLine("User Validated.");
else
Console.WriteLine("Login credentials incorrect. User not validated.");
}
}
Please advise. Thank you in advance
Changed your code a bit but this works:
public class Encrypt
{
public HashedCredential SecurePassword(string password, string salt = "")
{
var saltValue = salt;
if (string.IsNullOrEmpty(salt))
{
saltValue = GenertateSalt();
}
// Salt the password
byte[] saltedPassword = Encoding.UTF8.GetBytes(saltValue + password);
// Hash the salted password using SHA256
SHA512Managed hashstring = new SHA512Managed();
byte[] hash = hashstring.ComputeHash(saltedPassword);
return new HashedCredential(saltValue, Convert.ToBase64String(hash));
}
private string GenertateSalt()
{
RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
byte[] salt = new byte[32];
csprng.GetBytes(salt);
return Convert.ToBase64String(salt);
}
}
public class HashedCredential
{
public string SaltValue { get; }
public string HashValue { get; }
public HashedCredential(string saltValue, string hashValue)
{
SaltValue = saltValue;
HashValue = hashValue;
}
}
[TestMethod]
public void GenerateSalt()
{
// Arrange
var sut = new Encrypt();
// Act
var result = sut.SecurePassword("Test");
var resultB = sut.SecurePassword("Test", result.SaltValue);
// Assert
Console.WriteLine($"resultA:'{result.HashValue}'");
Console.WriteLine($"resultB:'{resultB.HashValue}'");
Assert.AreEqual(result.HashValue, resultB.HashValue);
}
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);
}
I'm working with Entity Framework Code First Migrations and I'd like to have the Seed method in my Configuration.cs file create a default user for me. I'm storing the hashed user passwords in the database as varbinary(64) and the salt as varbinary(16). I copied the hash and salt of an existing user so the seed method can use it when creating the default user. The problem I'm having is converting the string representation of the password hash and salt into their corresponding sql data types. Here's what I've tried so far:
string hashString = "0x81E09FC75CFAB13F54DF1266ADCA53B9FAE45C1D80655C61DE88057846F9B61DC3ED257F2C7D7B73826F9DC0FFA 5FF987B1A594FD9DAE3DC492F5815E989CD34";
string saltString = "0x630FE0A0186365FF9CCBB0FA6161C08B";
Byte[] pbytes = Encoding.ASCII.GetBytes(hashString);
Byte[] sbytes = Encoding.ASCII.GetBytes(saltString);
context.Users.AddOrUpdate(p => p.FirstName,
new User
{
Id = Guid.NewGuid(),
FirstName = "John",
LastName = "Doe",
Username = "admin",
Email = "jdoe#test.com",
PasswordHash = pbytes,
PasswordSalt = sbytes,
RoleId = 1,
LastLoginDate = null
});
When I run update-database from the Package Manager Console, I get a "Validation failed for one or more entities" error. Just to test things, I copied my hashing code over and tried using that and it worked as expected. Obviously, I don't want to reuse this code in my seed method so I just need a way to convert the strings int Byte[] that the db will be happy with. I've included the code that works below.
byte[] saltBytes = new byte[16];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(saltBytes);
byte[] plainTextBytes = System.Text.Encoding.UTF8.GetBytes("admin");
byte[] plainTextWithSaltBytes = new byte[plainTextBytes.Length + saltBytes.Length];
plainTextBytes.CopyTo(plainTextWithSaltBytes, 0);
saltBytes.CopyTo(plainTextWithSaltBytes, plainTextBytes.Length);
var hash = new SHA512Managed();
byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
context.Users.AddOrUpdate(p => p.FirstName,
new User
{
Id = Guid.NewGuid(),
FirstName = "John",
LastName = "Doe",
Username = "admin",
Email = "jdoe#test.com",
PasswordHash = hashBytes,
PasswordSalt = saltBytes,
RoleId = 1,
LastLoginDate = null
});
Looks like you are trying to convert hexadecimal to Bytes not String to bytes.
So you want to start here....
How can I convert a hex string to a byte array?
but if you end up with a "normal" string to begin with...
public static byte[] Base64StringToByteArray(string base64String) {
return Convert.FromBase64String(base64String);
}
public static byte[] UnicodeStringToByteArray(this string source) {
return Encoding.Unicode.GetBytes(source);
}
public static byte[] UTF8StringToByteArray(this string source)
{
return Encoding.UTF8.GetBytes(source);
}