I have recently implemented Hashing for my passwords in a project I am working on, and I cant seem to figure out what is going wrong.
It seems that the HashPasswordForStoringInConfigFile() function is returning different values for the same password.
I have the following code implemented which actually closely resembles the recommended algorithm to use on the MSDN documentation.
I know that SHA1 hashing is not considered very safe, but this is for a research application, and I am not too worried about it at this point.
public const int DefaultSaltSize = 5;
private static string CreateSalt()
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buffer = new byte[DefaultSaltSize];
rng.GetBytes(buffer);
return Convert.ToBase64String(buffer);
}
public static string CreateHash(string password)
{
string salt = CreateSalt();
string saltAndPassword = String.Concat(password, salt);
string hashedPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(saltAndPassword,"SHA1");
hashedPassword = string.Concat(hashedPassword,salt);
return hashedPassword;
}
public static bool VerifyPassword(string username, string password,AccountDataContext context)
{
var user = context.UserAccounts.FirstOrDefault(p => p.UserName == username);
if (user != null)
{
string salt = user.Password.Substring(user.Password.Length - DefaultSaltSize);
string hashedPassword = CreateHash(password);
return hashedPassword.Equals(user.Password);
}
return false;
}
Simply put, If I have the following code.
string password1 = "password";
string password2 = "password";
var hashedPassword1 = CreateHash(password1);
var hashedPassword2 = CreateHash(password2);
var match = hashedPassword1.Equals(hashedPassword2);
//match should be True, but it is turning out False.
It seems that the FormsAuthenticationForStoringInConfigFile() is not returning the same hash for password1 and password2 in the CreateHash() method.
I understand with the salt applied they are not the same, but if you see in the code, I am removing the salt before comparing the two hashedPasswords for equality.
What could possibly be causing password1 and password2 from being hashed differently?
Your code has added salt (a random value) to the password before hashing. This is a good thing.
It means that if user A and user B use the same password, the password hashes will nevertheless be different.
Your VerifyPassword method is not using the original salt to hash the password for comparing - instead it calls CreateHash, which calls CreateSalt and creates new salt.
You might try something like:
public static string CreateHash(string password)
{
return CreateHash(password, CreateSalt());
}
private static string CreateHash(string password, string salt)
{
string saltAndPassword = String.Concat(password, salt);
string hashedPassword =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPassword,"SHA1");
hashedPassword = string.Concat(hashedPassword,salt);
return hashedPassword;
}
public static bool VerifyPassword(string username,
string password,AccountDataContext context)
{
var user = context.UserAccounts.FirstOrDefault(p => p.UserName == username);
if (user != null)
{
string salt = user.Password.Substring(user.Password.Length - DefaultSaltSize);
string hashedPassword = CreateHash(password, salt);
return hashedPassword.Equals(user.Password);
}
return false;
}
Even though VerifyPassword looks like it's stripping off the salt portion of the unhashed string, but the code you say should return true doesn't actually call VerifyPassword.
Your code simply generates two salted hashes and then uses String.Equals to compare them.
What happens when you use VerifyPassword instead of String.Equals?
This code doesn't work at all either. Why is it marked as being the correct answer?
The default length of Salt is set to 5
Create Salt when it takes a 5 byte array to a string it becomes 8 characters not 5
Verify Password then takes only 5 characters off for the salt not 8 so the verify will always fail as it's using 5 characters for the salt and not the 8 that was used to create the hashed password.
Below is updated code to make the above code work.
private const int DEFAULT_SALT_SIZE = 5;
private static string CreateSalt()
{
RNGCryptoServiceProvider rngCryptoServiceProvider = new RNGCryptoServiceProvider();
byte[] buffer = new byte[DEFAULT_SALT_SIZE];
rngCryptoServiceProvider.GetBytes(buffer);
return Convert.ToBase64String(buffer);
}
public static string CreateHash(string password)
{
return CreateHash(password, CreateSalt());
}
private static string CreateHash(string password, string salt)
{
string saltAndPassword = String.Concat(password, salt);
string hashedPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(saltAndPassword, "SHA1");
hashedPassword = string.Concat(hashedPassword, salt);
return hashedPassword;
}
public static bool VerifyPassword(string userpassword, string password)
{
byte[] bytePassword = Convert.FromBase64String(userpassword);
byte[] byteSalt = new byte[DEFAULT_SALT_SIZE];
Array.Copy(bytePassword, bytePassword.Length - DEFAULT_SALT_SIZE, byteSalt, 0, DEFAULT_SALT_SIZE);
string salt = Convert.ToBase64String(byteSalt);
string hashedPassword = CreateHash(password, salt);
return hashedPassword.Equals(userpassword);
}
This is how I called it.
string hashedPassword = Security.CreateHash("password");
if (Security.VerifyPassword(hashedPassword, "password"))
{
Response.Write("Valid");
}
else
{
Response.Write("Not Valid");
}
As long as the passwords match it returns true otherwise it will return false.
This gives you what I think was intended which was not only to include the salt inside of the hash but also add it to the outside of the hash so it all could be stored as 1 column value in a database and then used to recreate the hash on the user entered password using the stored salt value and get a match.
Related
what I want to achieve:
I want to make an account system with login and register. I already have the login and register system, this is also connected to the database. However, the passwords are not hashed.
I can't find any help on how to do the hashing in connection with the database.
In addition, I still wonder how I add a little salt, since this would be safer.
Problem:
See above
code for login:
private void btn_login_Click(object sender, RoutedEventArgs e)
{
SqlConnection sqlCon = new SqlConnection("Server=xxxxx;Database=x;User Id=xxx;Password=xx;");
try
{
if (sqlCon.State == System.Data.ConnectionState.Closed)
sqlCon.Open();
String query = "SELECT COUNT(1) FROM tblUser WHERE Username=#Username AND Password=#Password";
SqlCommand sqlCmd = new SqlCommand(query, sqlCon);
sqlCmd.CommandType = System.Data.CommandType.Text;
sqlCmd.Parameters.AddWithValue("#Username", txtUsername.Text);
sqlCmd.Parameters.AddWithValue("#Password", txtPassword.Text);
int count = Convert.ToInt32(sqlCmd.ExecuteScalar());
if (count == 1)
{
MessageBox.Show("Success!");
}
else
{
MessageBox.Show("Wrong!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
sqlCon.Close();
}
}
Hash Stuff I want to implement
get SHA:
private static byte[] GetSHA1(string userID, string password)
{
SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
return sha.ComputeHash(System.Text.Encoding.ASCII.GetBytes(userID + password));
}
match SHA:
private static bool MatchSHA1(byte[] p1, byte[] p2)
{
bool result = false;
if (p1 != null && p2 != null)
{
if (p1.Length == p2.Length)
{
result = true;
for (int i = 0; i < p1.Length; i++)
{
if (p1[i] != p2[i])
{
result = false;
break;
}
}
}
}
return result;
}
test:
private static void RunTest()
{
string userId = "OriginalGriff";
string password = "NotMyPassword";
string enteredPassword = "NotMyPassword";
string notPassword = "notMyPassword";
byte[] hashedPassword = GetSHA1(userId, password);
if (MatchSHA1(hashedPassword, GetSHA1(userId, enteredPassword)))
{
Console.WriteLine("Log him in!");
}
else
{
Console.WriteLine("Don't log him in!");
}
if (MatchSHA1(hashedPassword, GetSHA1(userId, notPassword)))
{
Console.WriteLine("Will not happen!");
}
else
{
Console.WriteLine("Don't log him in!");
}
}
However, the passwords are not hashed. I can't find any help on how to do the hashing in connection with the database.
You are already familiar with how to store a string to the database here:
sqlCmd.Parameters.AddWithValue("#Password", txtPassword.Text);
Since you know how to store the password as a string in the database, and you also know how to hash the password with GetSHA1() - all we have to do is turn the hashed password into a string.
GetSHA1 currently returns a byte[] which is the hashed password and username.
In order to turn the byte[] into a string we should Encode(format the bytes into human readable text) it from bytes into a string. We're going to use the System.Convert.ToBase64String() method that takes any byte[] and turns it into a string.
Once we have encoded the hashed bytes into a string we don't have to use MatchSHA1 any more and can delete that method. This is because we can compare strings with the == operator or using other built-in methods.
If we can turn the hash into a string instead of
if (MatchSHA1(hashedPassword, GetSHA1(userId, enteredPassword)))
{
Console.WriteLine("Log him in!");
}
we instead could use a much simpler
if (hashedPassword == SHA384(userId, enteredPassword))
{
Console.WriteLine("Log him in!");
}
We can further strengthen the way we hash by upgrading to SHA384 since SHA1 is no longer recommended for use because of it's high collision rate.
I went ahead and modified your GetSHA1 method below with the recommended changes
private static string GetSHA384(string userID, string password)
{
// SHA classes are disposable, use using to ensure any managed resources are properly disposed of by the runtime
using SHA384 sha = new SHA384CryptoServiceProvider();
// convert the username and password into bytes
byte[] preHash = Encoding.ASCII.GetBytes(userID + password);
// hash the bytes
byte[] hash = sha.ComputeHash(preHash);
// convert the raw bytes into a string that we can save to a database
return Convert.ToBase64String(hash);
}
In order to store the password just replace
sqlCmd.Parameters.AddWithValue("#Password", txtPassword.Text);
with the following
sqlCmd.Parameters.AddWithValue("#Password", GetSHA384(txtUsername.Text, txtPassword.Text));
I still wonder how I add a little salt
If this is a passion project
SHA does not include an implementation of salting, for this you would have to make it yourself. I will leave this as an exercise for the reader or other helpful developers.
If this is a production project
I would recommend using an off-the-shelf implementation like BCrypt or something similar. Since implementing a secure salt and hash function yourself may lead to security vulnerabilities I do not recommend self-implementing them.
I'm registering clients in my application using this code,
string query= "INSERT INTO clientes (username,morada, sexo, telemovel, nif,password,email) " +
"VALUES(#username,#morada,#sexo,#telemovel,#nif,#password,#email)";
if(a.open_connection())
{
MySqlCommand cmd = new MySqlCommand(query, a.connection);
cmd.Parameters.AddWithValue("#username", textBox1.Text);
cmd.Parameters.AddWithValue("#morada", textBox2.Text);
cmd.Parameters.AddWithValue("#sexo", comboBox1.Text);
cmd.Parameters.AddWithValue("#telemovel", maskedTextBox2.Text);
cmd.Parameters.AddWithValue("#nif", maskedTextBox1.Text);
cmd.Parameters.AddWithValue("#email", textBox7.Text);
cmd.Parameters.AddWithValue("#password", textBox4.Text);
cmd.ExecuteNonQuery();
a.close_connection();
}
my question is how can i encrypt the field password?
What everyone is saying is correct, it is good practice to Hash passwords rather than encrypt them.
Here is how you can use it for yours.
private const int SHAVALUE = 16; // Change this number to whatever you want. It's like your key
private const int CB = 20; // You can change this two if you want (For extra security, maybe)
private static string GetPasswordReadyForDatabaseStorage(string password)
{
var salt = new byte[SHAVALUE];
//Create the salt value with a cryptographic PRNG:
new RNGCryptoServiceProvider().GetBytes(salt);
//Create the Rfc2898DeriveBytes and get the hash value:
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
var hash = pbkdf2.GetBytes(CB);
//Combine the salt and password bytes for later use:
var hashBytes = new byte[SHAVALUE+CB];
Array.Copy(salt, 0, hashBytes, 0, SHAVALUE);
Array.Copy(hash, 0, hashBytes, SHAVALUE, CB);
//Turn the combined salt+hash into a string for storage
return Convert.ToBase64String(hashBytes);
}
private static bool VerifyPassword(string passwordUserEntered)
{
/* Fetch the stored value */
string getPasswordHash = savedPasswordHash;//<--- Get the hash password from the database and place it here.
/* Extract the bytes */
var hashBytes = Convert.FromBase64String(getPasswordHash);
/* Get the salt */
var salt = new byte[SHAVALUE];
Array.Copy(hashBytes, 0, salt, 0, SHAVALUE);
/* Compute the hash on the password the user entered */
var pbkdf2 = new Rfc2898DeriveBytes(passwordUserEntered, salt, 10000);
var hash = pbkdf2.GetBytes(CB);
/* Compare the results */
for (int i = 0; i < CB; i++)
if (hashBytes[i + SHAVALUE] != hash[i])
{
return false;
}
return true;
}
You can go even further and use SecureStrings instead of string parameters.
Hope this helps!
Here is the link to where i referenced this code
https://stackoverflow.com/a/10402129/251311
I don't have deep information about cryptography ,but as I gathered some information,as an asymmetric solution I want to use RSA and here is the code which I found,I want to store an encrypted password into database and decrypt it whenever I want to get the user information,I mean these two actions don't happen respectively and whenever I like I can select from database and decrypt the previously save data.I know that there is something wrong with the generated keys,but because of low knowledge on this subject I cannot handle it.
I get the following Exception on decryption.
The parameter is incorrect.
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.RSACryptoServiceProvider.DecryptKey(SafeKeyHandle pKeyContext, Byte[] pbEncryptedKey, Int32 cbEncryptedKey, Boolean fOAEP, ObjectHandleOnStack ohRetDecryptedKey)
at System.Security.Cryptography.RSACryptoServiceProvider.Decrypt(Byte[] rgb, Boolean fOAEP)
Encryption Class:
public static class AsymmetricEncryption
{
private static bool _optimalAsymmetricEncryptionPadding = false;
public static void GenerateKeys(int keySize, out string publicKey, out string publicAndPrivateKey)
{
using (var provider = new RSACryptoServiceProvider(keySize))
{
publicKey = provider.ToXmlString(false);
publicAndPrivateKey = provider.ToXmlString(true);
}
}
public static string EncryptText(string text, int keySize, string publicKeyXml)
{
var encrypted = Encrypt(Encoding.UTF8.GetBytes(text), keySize, publicKeyXml);
return Convert.ToBase64String(encrypted);
}
public static byte[] Encrypt(byte[] data, int keySize, string publicKeyXml)
{
if (data == null || data.Length == 0) throw new ArgumentException("Data are empty", "data");
int maxLength = GetMaxDataLength(keySize);
if (data.Length > maxLength) throw new ArgumentException(String.Format("Maximum data length is {0}", maxLength), "data");
if (!IsKeySizeValid(keySize)) throw new ArgumentException("Key size is not valid", "keySize");
if (String.IsNullOrEmpty(publicKeyXml)) throw new ArgumentException("Key is null or empty", "publicKeyXml");
using (var provider = new RSACryptoServiceProvider(keySize))
{
provider.FromXmlString(publicKeyXml);
return provider.Encrypt(data, _optimalAsymmetricEncryptionPadding);
}
}
public static string DecryptText(string text, int keySize, string publicAndPrivateKeyXml)
{
var decrypted = Decrypt(Convert.FromBase64String(text), keySize, publicAndPrivateKeyXml);
return Encoding.UTF8.GetString(decrypted);
}
public static byte[] Decrypt(byte[] data, int keySize, string publicAndPrivateKeyXml)
{
if (data == null || data.Length == 0) throw new ArgumentException("Data are empty", "data");
if (!IsKeySizeValid(keySize)) throw new ArgumentException("Key size is not valid", "keySize");
if (String.IsNullOrEmpty(publicAndPrivateKeyXml)) throw new ArgumentException("Key is null or empty", "publicAndPrivateKeyXml");
using (var provider = new RSACryptoServiceProvider(keySize))
{
provider.FromXmlString(publicAndPrivateKeyXml);
return provider.Decrypt(data, _optimalAsymmetricEncryptionPadding);
}
}
public static int GetMaxDataLength(int keySize)
{
if (_optimalAsymmetricEncryptionPadding)
{
return ((keySize - 384) / 8) + 7;
}
return ((keySize - 384) / 8) + 37;
}
public static bool IsKeySizeValid(int keySize)
{
return keySize >= 384 &&
keySize <= 16384 &&
keySize % 8 == 0;
}
}
Encrypt usage:
const int keySize = 1024;
string publicAndPrivateKey;
string publicKey;
AsymmetricEncryption.GenerateKeys(keySize, out publicKey, out publicAndPrivateKey);
string text = "text for encryption";
string encrypted = AsymmetricEncryption.EncryptText(text, keySize, publicKey);
Decrypt usage:
const int keySize = 1024;
string publicAndPrivateKey;
string publicKey;
AsymmetricEncryption.GenerateKeys(keySize, out publicKey, out publicAndPrivateKey);
string text = "text for encryption";
string encrypted = AsymmetricEncryption.DecryptText(text, keySize, publicKey);
I read the encrypted text from a table.And I like to decrypt it.
Please help me solve this issue.
Thanks in advance
Snippet link
Let me stop you right here, before you actually figure out the exception.
IT IS A HORRIBLE IDEA TO STORE PASSWORDS REVERSIBLY, regardless what encryption you use. There are memory scrubbers and a million other problems with that scheme.
Passwords are to be HASHED, NOT ENCRYPTED. The difference is that hashes are hard to reverse if implemented properly. Use BCrypt or SHA2 or SHA3 or some other good hashing algorithms, use proper salt.
Here's an example, but consult someone knowledgeable before using in production, I may have bugs, the methods are coded for simplicity and may be too simple:
public string GetHash(string secret, string salt = null)
{
var hasher = SHA256Managed.Create();
salt = salt ?? Guid.NewGuid().ToString("N"); // 128 bit salt
var hashResult = hasher.ComputeHash(Encoding.UTF8.GetBytes(salt+"|"+what));
return "SHA256|" + salt + "|" + Convert.ToBase64String(hashResult);
}
Now you can store the string you get from this function in your DB and the next time a password comes in (over HTTPS, I hope) you can check it by getting the salt from the string stored in DB:
public bool CheckSecretHash(string secret, string hashFromDB){
var hashParts = hashFromDB.Split('|'); // string[3]
var rehash = GetHash(secret, hashParts[1]);
return hashFromDB == rehash;
}
Bonus points: challenge-response. ALWAYS USE HTTPS.
First I made textbox1(for username) , textbox2(for password) and button1(check).
After:
private void button1_Click(object sender, EventArgs e)
{
FileStream fs = new FileStream(#"D:\C#\test.txt", FileMode.Open, FileAccess.Read, FileShare.None);
StreamReader sr = new StreamReader(fs);
}
I want to check username from the first line of test.txt (equals like added from me text in textbox1) and password from the second line.
Sorry for my bad english.
You could try something like this:
private void button1_Click(object sender, EventArgs e){
string[] lines = System.IO.File.ReadAllLines(#"D:\C#\test.txt");
String username = lines[0];
String password = lines[1];
}
However, it's not a very good way to store your usernames and passwords. I'm assuming you're just testing something out.
The simplest answer to your question is to read the text-file line by line. I would however strongly suggest at least seeding and hashing the password.
This is a short example utilizing seeded SHA256 hashed passwords. It's just to show the concept and should not be used as is.
void Test()
{
string pwd = "Testing1234";
string user = "username";
StorePassword(user, pwd);
bool result = ValidatePassword(user, pwd);
if (result == true) Console.WriteLine("Match!!");
}
private void StorePassword(string username, string password)
{
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var salt = new string(
Enumerable.Repeat(chars, 8)
.Select(s => s[random.Next(s.Length)])
.ToArray());
string hash = GetHash(salt + password);
string saltedHash = salt + ":" + hash;
string[] credentials = new string[] { username, saltedHash };
System.IO.File.WriteAllLines(#"D:\C#\test.txt",credentials);
}
bool ValidatePassword(string username, string password)
{
string[] content = System.IO.File.ReadAllLines(#"D:\C#\test.txt");
if (username != content[0]) return false; //Wrong username
string[] saltAndHash = content[1].Split(':'); //The salt will be stored att index 0 and the hash we are testing against will be stored at index 1.
string hash = GetHash(saltAndHash[0] + password);
if (hash == saltAndHash[1]) return true;
else return false;
}
string GetHash(string input)
{
System.Security.Cryptography.SHA256Managed hasher = new System.Security.Cryptography.SHA256Managed();
byte[] bytes = hasher.ComputeHash(Encoding.UTF8.GetBytes(input));
return BitConverter.ToString(bytes).Replace("-", "");
}
I am currently working on a Log in form but the thing here is that when I enter an invalid username this errors comes up.
Invalid length for a Base-64 char array.
On this line of code:
bool isSame = hasher.CompareStringToHash(txtPassword.Text, hashedPassword);
Here is the full code
public void verifyAccount()
{
var hashedPassword = getPassword();
var hasher = new Hasher();
hasher.SaltSize = 16;
bool isSame = hasher.CompareStringToHash(txtPassword.Text, hashedPassword);
if (isSame==false)
{
MessageBox.Show("Invalid UserName or Password");
}
else
{
MainWindow main = new MainWindow();
this.Hide();
main.ShowDialog();
this.Close();
}
}
And the This is the method for searching a such username at the same time getting their password password in the database.
public string getPassword()
{
DataClasses1DataContext myDbContext = new DataClasses1DataContext(dbPath);
var password = (from user in myDbContext.Accounts
where user.accnt_User == txtUser.Text
select user.accnt_Pass).FirstOrDefault();
if (password == null) {
return "z";
}
return password;
}
Base64 data should be 4-bytes aligned. So for example the "z" that you're returning should be Base64 encoded and padded like this: "eg==" to be Base64 compliant. You should do the same type of padding for the real hashed password.
hasher.CompareStringToHash probably expects the hashedPassword to be a proper Base64-encoded string. The "z" string you use is invalid in this case.
I'd suggest changing your code like this:
private void VerifyAccount()
{
if (!ValidateCredentials(txtUser.Text, txtPassword.Text))
{
MessageBox.Show("Invalid user name or password");
}
}
private bool ValidateCredentials(string userName, string password)
{
string existingPassword = GetUserPassword(userName);
if (existingPassword == null)
return false;
var hasher = new Hasher { SaltSize = 16 };
bool passwordsMatch = hasher.CompareStringToHash(password, existingPassword);
return passwordsMatch;
}
private string GetUserPassword(string userName)
{
DataClasses1DataContext dataContext = new DataClasses1DataContext();
var password = (from user in dataContext.Accounts
where user.accnt_User == userName
select user.accnt_Pass).FirstOrDefault();
return password;
}
You should just let GetPassword() return null if the password isn't found. The next problem though is that CompareStringToHash() will fail if you pass in a null password, so instead of
bool isSame = hasher.CompareStringToHash(txtPassword.Text, hashedPassword);
if (isSame==false)
{
MessageBox.Show("Invalid UserName or Password");
}
You do
if (hashedPassword == null || hasher.CompareStringToHash(txtPassword.Text, hashedPassword)
{
MessageBox.Show("Invalid UserName or Password");
}
If hashedPassword is null the if statement will be exited before it tries to execute the CompareStringToHash() so you won't get an exception. CompareStringToHash() will only execute if hashedPassword is not null. Then provided you've stored a valid string (which you will have because you are creating it will Encrypto) it should all work - and without a lot of messy if statements :o)