So I need to store passwords in a SQL database and it would be insecure to store them in plain text. For a variety of reasons, I chose SHA512 to hash the passwords prior to storage. I, for the life of me, can not identify how to take data from a Secure string gained from user input, and hash it using SHA512 (which also means I haven't been able to look into salting it either).
I have seen online that you call a new instance of SHA512 but that it has to be managed (?) but when I try it shows that it is obsolete. Looking further, the wise internet suggested the create method of SHA512... which is also obsolete.
Any help into how I can hash and salt a secure string would be great.
Here is a basic hash and salt method using SHA512 and a random salt prepended to the hash.
private static string HashAndSalt(string plaintext)
{
var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
using var hasher = SHA512.Create();
var random = new Random();
var ciphertextBytes = hasher.ComputeHash(Encoding.UTF8.GetBytes(plaintext));
var ciphertextB64 = Convert.ToBase64String(ciphertextBytes);
var salt = new string(Enumerable.Repeat(chars, 8).Select(s => s[random.Next(s.Length)]).ToArray());
var ciphertext = salt + ':' + ciphertextB64;
return ciphertext;
}
You will need to convert your SecureString to a normal string. Using a SecureString is obsolete and should not be used anymore. As the comments mentioned you should look into a dedicated password hashing algorithm such as:
PBKDF2
Argon2
Bcrypt
Scrypt
Here is an example using Bcrypt - it is not too complicated.
First, grab this NuGet package: BCrypt.Net-Next
private static string BcryptHash(string plaintext)
{
var ciphertext = BCrypt.Net.BCrypt.HashPassword(plaintext, 12);
return ciphertext;
}
You can't however compare the hashes when a user tries to authenticate as you can with SHA512. You need to use Bcrypts 'Verify' function.
private static bool BcryptVerify(string plaintext)
{
return BCrypt.Net.BCrypt.Verify(plaintext, hashedPassword);
}
Where the plaintext is the plaintext password you receive from user input and the hashedPassword being the hash you retrieved from the database where the initial ciphertext was stored.
Hope this helps.
I think this is what you are asking?
public static string GetHash(string s)
{
using var h = SHA512.Create();
return Convert.ToBase64String(h.ComputeHash(Encoding.UTF8.GetBytes(s)));
}
Have a look here for some other inspirations of how to do this:
https://github.com/aspnet/AspNetWebStack/blob/main/src/System.Web.Helpers/Crypto.cs
Related
I have the following problem that I've been dealing for a couple of hours by now and it's driving nuts.
Context
I have a legacy database that stored passwords using the following algorithm. The legacy code used a Python library.
PBKDF2 with SHA256
1000 iterations
Salt has a length of 8
Password is stored like this $salt$hashedPassword
I'm switching login flow for the new system and I need to migrate that old algorithm to a new one. New system uses .netcore
Question
What I'm trying to do is even possible?. How can I achieve it?
What my logic dictates is that I can take the salt and recreate the hashing algorithm using .netcore Crypto library but its not working and the function returns always false.
Legacy Code
from werkzeug.security import generate_password_hash, check_password_hash
def setPassword(self, password):
self.password = generate_password_hash(password, method='pbkdf2:sha256')
Where generate_password_hash comes from the library, this is the code
SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
def generate_password_hash(password, method="pbkdf2:sha256", salt_length=8):
"""Hash a password with the given method and salt with a string of
the given length. The format of the string returned includes the method
that was used so that :func:`check_password_hash` can check the hash.
The format for the hashed string looks like this::
method$salt$hash
This method can **not** generate unsalted passwords but it is possible
to set param method='plain' in order to enforce plaintext passwords.
If a salt is used, hmac is used internally to salt the password.
If PBKDF2 is wanted it can be enabled by setting the method to
``pbkdf2:method:iterations`` where iterations is optional::
pbkdf2:sha256:80000$salt$hash
pbkdf2:sha256$salt$hash
:param password: the password to hash.
:param method: the hash method to use (one that hashlib supports). Can
optionally be in the format ``pbkdf2:<method>[:iterations]``
to enable PBKDF2.
:param salt_length: the length of the salt in letters.
"""
salt = gen_salt(salt_length) if method != "plain" else ""
h, actual_method = _hash_internal(method, salt, password)
return "%s$%s$%s" % (actual_method, salt, h)
def gen_salt(length):
"""Generate a random string of SALT_CHARS with specified ``length``."""
if length <= 0:
raise ValueError("Salt length must be positive")
return "".join(_sys_rng.choice(SALT_CHARS) for _ in range_type(length))
Code
using System;
using System.Security.Cryptography;
using System.Text;
namespace test_pwd
{
class Program
{
static void Main(string[] args)
{
var res = SameHash("Qwerty12", "84e8c8a5dbdafaf23523ffa5dfecf29d53522a35ca4c76fa877c5fcf9eb4b654", "laSgSC6R");
Console.WriteLine(res);
}
public static bool SameHash(string userpwd, string storedHash, string storedSalt)
{
var saltByte = Encoding.UTF8.GetBytes(storedSalt);
var rfc = new Rfc2898DeriveBytes(userpwd, saltByte, 1000);
var baseString = Convert.ToBase64String(rfc.GetBytes(64));
return baseString == storedHash;
}
}
}
Base string is converted into
k6vhCweBNz8ymMeEdhi+1czrea+oTTYLrW1OuwdinA78AFyEXKitpKUGLCt1ZdyS1Vka8Cptzd5u5Uzdbi4MbA==
Which is not the same as the stored password hash I'm sending. What I'm doing wrong or this idea is even feasible?.
I have a string that needs to be hashed using a salt in C# and match what it would in PHP. The C# code is as follows:
string stringToHash = "123";
string saltToUse = "321";
byte[] stringBytes = ASCIIEncoding.ASCII.GetBytes(stringToHash);
byte[] saltBytes = ASCIIEncoding.ASCII.GetBytes(saltToUse);
var hmacMD5 = new HMACMD5(saltBytes);
var saltedHash = hmacMD5.ComputeHash(stringBytes);
byte[] hashedBytesNoSalt = MD5CryptoServiceProvider.Create().ComputeHash(stringBytes);
string hashedString = BitConverter.ToString(hashedBytesNoSalt).Replace("-", "").ToLower();
string saltedString = BitConverter.ToString(saltedHash).Replace("-", "").ToLower();
The PHP code to test if the C# is outputting correctly is:
<?php echo md5('123'.'321'); ?>
The C# outputs the correct UNSALTED MD5 hash, which is 202cb962ac59075b964b07152d234b70. However, when I try to salt using C# I get 900011ae860f471561023fba6cc25df6 and with PHP I get c8837b23ff8aaa8a2dde915473ce0991.
I am not sure why it is doing this or if this is even the correct way. The thing to remember is the C# needs to output to what the PHP outputs to.
The C# code is using a better salting mechanism than the PHP code. To match the PHP, all you need to do is run MD5 on stringToHash + saltToUse instead of using the HMACMD5's key feature. In short, do exactly what you did to produce your unsalted MD5 in C#, but pass in stringToHash + saltToUse instead.
A salt is not the same thing as a private key. HMACMD5 uses a private key, when all that you desire is a salted MD5 hash. Just append the salt to the string to generate the correct key... stringToHash + saltToUse.
Read about HMACMD5 here: http://msdn.microsoft.com/en-us/library/yd9e7dt2.aspx
I have a PHP web service that I've discovered is passing my C# a SHA-1 encrupted value. The sample data that is passed to me is "8cb2237d0679ca88db6464eac60da96345513964" which I know translates to "12345".
How do I translate the hashed value back to "12345" with code similar to the following
public static string HashCode(string str)
{
string rethash = "";
try
{
System.Security.Cryptography.SHA1 hash = System.Security.Cryptography.SHA1.Create();
System.Text.ASCIIEncoding encoder = new System.Text.ASCIIEncoding();
byte[] combined = encoder.GetBytes(str);
hash.ComputeHash(combined);
rethash = Convert.ToBase64String(hash.Hash);
}
catch (Exception ex)
{
string strerr = "Error in HashCode : " + ex.Message;
}
return rethash;
}
EDIT *
Here is some RUBY code that is also workig with "8cb2237d0679ca88db6464eac60da96345513964" and "12345"
require "digest/sha1"
class User
attr_accessor :password
def initialize(password)
#password = hash_password(password)
end
def hash_password(password)
Digest::SHA1.hexdigest(password)
end
def valid_password?(password)
#password == hash_password(password)
end
end
u = User.new("12345")
p u.password # => "8cb2237d0679ca88db6464eac60da96345513964"
p u.valid_password?("not valid") # => false
p u.valid_password?("12345") # => true
You can't decrypt SHA1 hash because it's a one way hash.
Another example of one way hashing is MD5
The ruby code that you posted doesn't appear to be reversing a hash.
What it seems to be doing is this:
Get the password text, hash it and store it.
Later, when it wants to check that the "user" entered the same password again, it gets the password text from the user, hashes it, and compares the hash value to the stored hash value.
This is a common way to store and check passwords. Instead of "dehashing" the stored value for comparison, you hash the new value and compare the two hash values.
12345 will always come out as 8cb2237d0679ca88db6464eac60da96345513964 with a straight hash.
This means that if you made a database of every possible result, you could in theory look up the result and from that see what the original input to the sha1 function was.
This is a security problem, with issues like Dictionary Attacks and Rainbow tables being possible (http://en.wikipedia.org/wiki/Rainbow_table).
To get around that, you should never use an unsalted hash. i.e. you always customise your hash using a value known to you.
For example sha1("12345" + "mySalt").
Now your hash is easy for you to work out, but not the same as every other person in the world who has used sha1.
Technically speaking, you should also never reuse the same salt twice either, but that is a more complicated concept.
EDIT: As owlstead points out below, PBKDF2 and a random salt should be used, rather than a static one and a hash. Far better for security.
The code you are looking for is this
SHA1 sha = new SHA1CryptoServiceProvider();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] combined = encoder.GetBytes(pin);
string hash = BitConverter.ToString(sha.ComputeHash(combined)).Replace("-", "");
Where pin is the unhashed value, and hash is the value you want compaired
Hashing is not a reversible operation, like encryption.
Hashing is not encryption. Hashing is one way, and is used in most cases to verify data integrity.
I try to hash a string using SHA256, I'm using the following code:
using System;
using System.Security.Cryptography;
using System.Text;
public class Hash
{
public static string getHashSha256(string text)
{
byte[] bytes = Encoding.Unicode.GetBytes(text);
SHA256Managed hashstring = new SHA256Managed();
byte[] hash = hashstring.ComputeHash(bytes);
string hashString = string.Empty;
foreach (byte x in hash)
{
hashString += String.Format("{0:x2}", x);
}
return hashString;
}
}
However, this code gives significantly different results compared to my friends php, as well as online generators (such as This generator)
Does anyone know what the error is? Different bases?
Encoding.Unicode is Microsoft's misleading name for UTF-16 (a double-wide encoding, used in the Windows world for historical reasons but not used by anyone else). http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx
If you inspect your bytes array, you'll see that every second byte is 0x00 (because of the double-wide encoding).
You should be using Encoding.UTF8.GetBytes instead.
But also, you will see different results depending on whether or not you consider the terminating '\0' byte to be part of the data you're hashing. Hashing the two bytes "Hi" will give a different result from hashing the three bytes "Hi". You'll have to decide which you want to do. (Presumably you want to do whichever one your friend's PHP code is doing.)
For ASCII text, Encoding.UTF8 will definitely be suitable. If you're aiming for perfect compatibility with your friend's code, even on non-ASCII inputs, you'd better try a few test cases with non-ASCII characters such as é and 家 and see whether your results still match up. If not, you'll have to figure out what encoding your friend is really using; it might be one of the 8-bit "code pages" that used to be popular before the invention of Unicode. (Again, I think Windows is the main reason that anyone still needs to worry about "code pages".)
I also had this problem with another style of implementation but I forgot where I got it since it was 2 years ago.
static string sha256(string randomString)
{
var crypt = new SHA256Managed();
string hash = String.Empty;
byte[] crypto = crypt.ComputeHash(Encoding.ASCII.GetBytes(randomString));
foreach (byte theByte in crypto)
{
hash += theByte.ToString("x2");
}
return hash;
}
When I input something like abcdefghi2013 for some reason it gives different results and results in errors in my login module.
Then I tried modifying the code the same way as suggested by Quuxplusone and changed the encoding from ASCII to UTF8 then it finally worked!
static string sha256(string randomString)
{
var crypt = new System.Security.Cryptography.SHA256Managed();
var hash = new System.Text.StringBuilder();
byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(randomString));
foreach (byte theByte in crypto)
{
hash.Append(theByte.ToString("x2"));
}
return hash.ToString();
}
Thanks again Quuxplusone for the wonderful and detailed answer! :)
public static string ComputeSHA256Hash(string text)
{
using (var sha256 = new SHA256Managed())
{
return BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(text))).Replace("-", "");
}
}
The reason why you get different results is because you don't use the same string encoding. The link you put for the on-line web site that computes SHA256 uses UTF8 Encoding, while in your example you used Unicode Encoding. They are two different encodings, so you don't get the same result. With the example above you get the same SHA256 hash of the linked web site. You need to use the same encoding also in PHP.
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/
public string EncryptPassword(string password, string saltorusername)
{
using (var sha256 = SHA256.Create())
{
var saltedPassword = string.Format("{0}{1}", salt, password);
byte[] saltedPasswordAsBytes = Encoding.UTF8.GetBytes(saltedPassword);
return Convert.ToBase64String(sha256.ComputeHash(saltedPasswordAsBytes));
}
}
New .NET 5+ solution:
If you're using .NET 5 or above, you can use the new Convert.ToHexString method to convert the hash byte array into a hexadecimal string; eliminating the hassle of using string builders and so on.
The following method also uses the using block so that the SHA256 instance gets disposed.
It also turns the password (which is passed in as a string) into a byte array using UTF-8 encoding, which was recommended by the accepted answer. Furthermore, we're also using the new SHA256 class as opposed to the old (now obselete) SHA256Managed class.
public string QuickHash(string secret)
{
using var sha256 = SHA256.Create();
var secretBytes = Encoding.UTF8.GetBytes(secret);
var secretHash = sha256.ComputeHash(secretBytes);
return Convert.ToHexString(secretHash);
}
Note: You should NOT use this method for hashing user passwords. General-purpose hashing functions such as SHA-256 aren't suited for use for passwords anymore, even if you add salts. This is useful for hashing strings that you know have high entropy, such as long randomly generated session tokens and whatnot. For storing passwords, you must look into slower hashing functions that were specifically designed for this purpose, such as Bcrypt, Scrypt, or PBKDF2 (the latter is available natively in .NET — see this)
The shortest and fastest way ever. Only 1 line!
public static string StringSha256Hash(string text) =>
string.IsNullOrEmpty(text) ? string.Empty : BitConverter.ToString(new System.Security.Cryptography.SHA256Managed().ComputeHash(System.Text.Encoding.UTF8.GetBytes(text))).Replace("-", string.Empty);
In the PHP version you can send 'true' in the last parameter, but the default is 'false'. The following algorithm is equivalent to the default PHP's hash function when passing 'sha256' as the first parameter:
public static string GetSha256FromString(string strData)
{
var message = Encoding.ASCII.GetBytes(strData);
SHA256Managed hashString = new SHA256Managed();
string hex = "";
var hashValue = hashString.ComputeHash(message);
foreach (byte x in hashValue)
{
hex += String.Format("{0:x2}", x);
}
return hex;
}
I was looking and testing theses answers, and Visual Studio showed me that SHA256Managed is now Obsolete (here)
So, I used the SHA256 class instead:
Encoding enc = Encoding.UTF8;
var hashBuilder = new StringBuilder();
using var hash = SHA256.Create();
byte[] result = hash.ComputeHash(enc.GetBytes(yourStringToHash));
foreach (var b in result)
hashBuilder.Append(b.ToString("x2"));
string result = hashBuilder.ToString();
This work for me in .NET Core 3.1.
But not in .NET 5 preview 7.
using System;
using System.Security.Cryptography;
using System.Text;
namespace PortalAplicaciones.Shared.Models
{
public class Encriptar
{
public static string EncriptaPassWord(string Password)
{
try
{
SHA256Managed hasher = new SHA256Managed();
byte[] pwdBytes = new UTF8Encoding().GetBytes(Password);
byte[] keyBytes = hasher.ComputeHash(pwdBytes);
hasher.Dispose();
return Convert.ToBase64String(keyBytes);
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
}
}
I have made a user registration where I have salted the user password and hashed it using SHA256.
later, when the user needs to log into my system I need to have his password salted and hashed, so I :
1.retrieved the salt "string" from Database
2. converted the salt into bytes
3. created a new byte[] = [inputPassword.length + salt.length]
4. and hashed that.
now the new hash is shorter than Original hash ...(using same hashing functions)
given these information what do you think the problem might be ...
is storing the salt as CHAR on my database wrong , if yes what or how should I save it ?
Note: both hashes are compared on byte level.
Note: all user information are stored in the database password and salt as CHAR
thank you in advance
You could generate a salt from a Guid converted into a base 64 string, then save that in the database as char. I use nvarchar to maximise my options using a .NET string.
Then you can implement something like this for generating the original password hash, and comparing the hash when the user logs in:
public static byte[] GetHash(string password, string salt)
{
byte[] unhashedBytes = Encoding.Unicode.GetBytes(String.Concat(salt, password));
SHA256Managed sha256 = new SHA256Managed();
byte[] hashedBytes = sha256.ComputeHash(unhashedBytes);
return hashedBytes;
}
public static bool CompareHash(string attemptedPassword, byte[] hash, string salt)
{
string base64Hash = Convert.ToBase64String(hash);
string base64AttemptedHash = Convert.ToBase64String(GetHash(attemptedPassword, salt));
return base64Hash == base64AttemptedHash;
}
Usually hash functions return fixed size hash, so if you tell that new hash is shorter I think problem might be in your hash function.