Related
I've been reading this article from MSDN on Rfc2898DeriveBytes. Here is the sample encryption code they provide.
string pwd1 = passwordargs[0];
// Create a byte array to hold the random value.
byte[] salt1 = new byte[8];
using (RNGCryptoServiceProvider rngCsp = ne RNGCryptoServiceProvider())
{
// Fill the array with a random value.
rngCsp.GetBytes(salt1);
}
//data1 can be a string or contents of a file.
string data1 = "Some test data";
//The default iteration count is 1000 so the two methods use the same iteration count.
int myIterations = 1000;
try
{
Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(pwd1,salt1,myIterations);
Rfc2898DeriveBytes k2 = new Rfc2898DeriveBytes(pwd1, salt1);
// Encrypt the data.
TripleDES encAlg = TripleDES.Create();
encAlg.Key = k1.GetBytes(16);
MemoryStream encryptionStream = new MemoryStream();
CryptoStream encrypt = newCryptoStream(encryptionStream, encAlg.CreateEncryptor(), CryptoStreamMode.Write);
byte[] utfD1 = new System.Text.UTF8Encoding(false).GetBytes(data1);
encrypt.Write(utfD1, 0, utfD1.Length);
encrypt.FlushFinalBlock();
encrypt.Close();
byte[] edata1 = encryptionStream.ToArray();
k1.Reset();
My question is, how would I properly Read/Write the hashed data to/from a text file?
My main goal is to do what this developer is doing. I need to store a password locally. When my application prompts the user for the password, the user will enter the password, then my application will read from the text file and verify if the password that the user entered is indeed correct. How would I go about doing it?
You typically store the hash of the password, then when user enters password, you compute hash over the entered password and compare it with the hash which was stored - that said, just hashing is usually not enough (from security point of view) and you should use a function such as PKBDF2 (Password-Based Key Derivation Function 2) instead. Here is article covering all that information in more elaborate way as well as sample code (bottom of the page): http://www.codeproject.com/Articles/704865/Salted-Password-Hashing-Doing-it-Right
Here is a link to codereview, which I guess refers to the same implementation as above article.
How to properly store password locally
Just don't do it. No really don't do it.
...But if you really really have to, never just implement it yourself. I would recommend reviewing how ASP.NET Identity hashes passwords. Version 3 is pretty rock solid at the moment:
note that the following is taken from github.com and may be changed at any time. For the latest, please refer to the previous link.
private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested)
{
// Produce a version 3 (see comment above) text hash.
byte[] salt = new byte[saltSize];
rng.GetBytes(salt);
byte[] subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested);
var outputBytes = new byte[13 + salt.Length + subkey.Length];
outputBytes[0] = 0x01; // format marker
WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount);
WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize);
Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
return outputBytes;
}
You should store the password as a one-way hash and the salt used to create that password. This way you are absolutely sure that the password for the user can never be DECRYPTED. Never use any two-way encryption for this particular task, as you risk exposing user information to would-be attackers.
void Main()
{
string phrase, salt, result;
phrase = "test";
result = Sha256Hash(phrase, out salt);
Sha256Compare(phrase, result, salt);
}
public string Sha256Hash(string phrase, out string salt)
{
salt = Create256BitSalt();
string saltAndPwd = String.Concat(phrase, salt);
Encoding encoder = Encoding.Default;
SHA256Managed sha256hasher = new SHA256Managed();
byte[] hashedDataBytes = sha256hasher.ComputeHash(encoder.GetBytes(saltAndPwd));
string hashedPwd = Encoding.Default.GetString(hashedDataBytes);
return hashedPwd;
}
public bool Sha256Compare(string phrase, string hash, string salt)
{
string saltAndPwd = String.Concat(phrase, salt);
Encoding encoder = Encoding.Default;
SHA256Managed sha256hasher = new SHA256Managed();
byte[] hashedDataBytes = sha256hasher.ComputeHash(encoder.GetBytes(saltAndPwd));
string hashedPwd = Encoding.Default.GetString(hashedDataBytes);
return string.Compare(hash, hashedPwd, false) == 0;
}
public string Create256BitSalt()
{
int _saltSize = 32;
byte[] ba = new byte[_saltSize];
RNGCryptoServiceProvider.Create().GetBytes(ba);
return Encoding.Default.GetString(ba);
}
You could also figure out another method for obtaining the salt, but I have made mine to that it computes 2048 bits worth of random data. You could just use a random long you generate but that would be a lot less secure. You won't be able to use SecureString because SecureString isn't Serializable. Which the whole point of DPAPI. There are ways to get the data out but you end up having to jump a few hurdles to do it.
FWIW, PBKDF2 (Password-Based Key Derivation Function 2) is basically the same thing as SHA256 except slower (a good thing). On its own both are very secure. If you combined PBKDF2 with an SHA256 as your salt then you'd have a very secure system.
I'm having trouble with WS-Security, and creating a nonce and password digest that is correct.
I am successfully using SoapUI to send data to an Oracle system. So I'm able to intercept SoapUI's call (change proxy to 127.0.0.1 port 8888 to use Fiddler where it fails because it's over SSL) - intercepting is important because these values can only be used once. I can then grab the nonce, created timestamp and password digest put them into my code (I've only got 30 seconds to do this as the values don't last!) and I get a success.
So I know it's nothing else - just the Password Digest.
The values I use are the following:
Nonce: UIYifr1SPoNlrmmKGSVOug==
Created Timestamp: 2009-12-03T16:14:49Z
Password: test8
Required Password Digest: yf2yatQzoaNaC8BflCMatVch/B8=
I know the algorithm for creating the Digest is:
Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
using the following code (from Rick Strahl's post)
protected string GetSHA1String(string phrase)
{
SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(phrase));
return Convert.ToBase64String(hashedDataBytes);
}
I get:
GetSHA1String("UIYifr1SPoNlrmmKGSVOug==" + "2009-12-03T16:14:49Z" + "test8") = "YoQKI3ERlMDGEXHlztIelsgL50M="
I have tried various SHA1 methods, all return the same results (which is a good thing I guess!):
SHA1 sha1 = SHA1.Create();
SHA1 sha1 = SHA1Managed.Create();
// Bouncy Castle:
protected string GetSHA1usingBouncyCastle(string phrase)
{
IDigest digest = new Sha1Digest();
byte[] resBuf = new byte[digest.GetDigestSize()];
byte[] bytes = Encoding.UTF8.GetBytes(phrase);
digest.BlockUpdate(bytes, 0, bytes.Length);
digest.DoFinal(resBuf, 0);
return Convert.ToBase64String(resBuf);
}
Any ideas on how to get the correct hash?
The problem was the nonce.
I was trying to use a nonce that had already been Base64 encoded. If you want to use a Nonce that is in the form "UIYifr1SPoNlrmmKGSVOug==" then you need to decode it.
Convert.FromBase64String("UIYifr1SPoNlrmmKGSVOug==")
which is a byte array.
So we need a new method:
public string CreatePasswordDigest(byte[] nonce, string createdTime, string password)
{
// combine three byte arrays into one
byte[] time = Encoding.UTF8.GetBytes(createdTime);
byte[] pwd = Encoding.UTF8.GetBytes(password);
byte[] operand = new byte[nonce.Length + time.Length + pwd.Length];
Array.Copy(nonce, operand, nonce.Length);
Array.Copy(time, 0, operand, nonce.Length, time.Length);
Array.Copy(pwd, 0, operand, nonce.Length + time.Length, pwd.Length);
// create the hash
var sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(operand);
return Convert.ToBase64String(hashedDataBytes);
}
CreatePasswordDigest(Convert.FromBase64String("UIYifr1SPoNlrmmKGSVOug=="), "2009-12-03T16:14:49Z", "test8")
which returns yf2yatQzoaNaC8BflCMatVch/B8= as we want.
Remember to use the same createdTime in the digest as you put in the XML, this might sound obvious, but some people include milliseconds on their timestamps and some don't - it doesn't matter, it just needs to be consistent.
Also the Id field in the UsernameToken XML doesn't matter - it doesn't need to change.
Here's a method to create a Nonce like the one above, if you don't want to use GUIDs like Rick uses:
private byte[] CreateNonce()
{
var Rand = new RNGCryptoServiceProvider();
//make random octets
byte[] buf = new byte[0x10];
Rand.GetBytes(buf);
return buf;
}
I hope that helps someone - it took me lots of frustration, trial and error, searching web pages, and general head/wall banging.
I have a table with usenames, hashed password and their salts, now in my application I want to verif the plain password with hashed one below is what I tried but does not generate the same hash, please suggest how can I solve this problem.
byte[] bIn = Encoding.Unicode.GetBytes(Password);
byte[] bSalt = Convert.FromBase64String(SaltValue);
byte[] bAll = new byte[bSalt.Length + bIn.Length];
Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
HMACSHA256 s = new HMACSHA256();
return Convert.ToBase64String(s.ComputeHash(bAll));
You should create one method to hash a password with a salt. Then use this method to encrypt the initial password. If you reuse this method verifying the password afterwards it will always match.
Make sure you retrieved the correct salt from your database for the account.
I looked up msdn and other resources on how to do this but i came up with no clear solutions. This is the best i found http://blogs.msdn.com/b/shawnfa/archive/2004/04/14/generating-a-key-from-a-password.aspx?Redirected=true
I would like to hash passwords in C# using either bcrypt or PBKDF2 (which appears to be bcrypt related). I like to experiment with how many rounds it takes for my computer to hash a password. However everything seems to be about encrypting while everyone talks about hashing. I can't figure it out. How do i hash a password? It looks more like PBKDF2 (Rfc2898?) is a random number generator and i use GetBytes(amount) to choose how big my hash size is.
I'm confused. How exactly do i hash a password with bcrypt/PBKDF?
PBKDF2
You were really close actually. The link you have given shows you how you can call the Rfc2898DeriveBytes function to get PBKDF2 hash results. However, you were thrown off by the fact that the example was using the derived key for encryption purposes (the original motivation for PBKDF1 and 2 was to create "key" derivation functions suitable for using as encryption keys). Of course, we don't want to use the output for encryption but as a hash on its own.
You can try the SimpleCrypto.Net library written for exactly this purpose if you want PBKDF2. If you look at the implementation, you can see that it is actually just a thin wrapper around (you guessed it) Rfc2898DeriveBytes.
BCrypt
You can try the C# implementation named (what else) BCrypt.NET if you want to experiment with this variant.
Disclaimer: I have not used or tested any of the libraries that I have linked to... YMMV
First of all, I urge everyone to use a cryptographically verified reference algorithm included with the platform itself.
Do not use 3rd party packages and non-verified OSS components or any other code you just copy-pasted from the Internet.
For .NET use PBKDF2 and not bCrypt because there's no certified implementation of bCrypt for .NET
I don't mean any disrespect for any noble open-source devs (being one myself), but you can never be sure their website won't be hacked in 10 years and you end up getting a malware package from Nuget/npm or other package managers.
More info about verification can be found in this SO answer
Now, back to PBKDF2, here's the simple code
public static byte[] PBKDF2Hash(string input, byte[] salt)
{
// Generate the hash
Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(input, salt, iterations: 5000);
return pbkdf2.GetBytes(20); //20 bytes length is 160 bits
}
If you need a string representation of the hash (not byte-array) - you can use this superfast conversion class from this answer http://stackoverflow.com/a/624379/714733
It took me forever (days it took days) to find what to actually code to get hashed passwords to work!! so I put it here for convenience.
You do need to read the documentation and theory1 theory2 and then some or you could be open to security loopholes. Security is a very big topic! Buyer Beware!
Add the NuGet Package BCrypt.Net to the solution
const int WorkFactor = 14;
var HashedPassword = BCrypt.Net.BCrypt.HashPassword(Password, WorkFactor);
You should adjust the WorkFactor to what is appropriate see discussions. Its a log2 function
"The number is log2, so every time computers double in speed, add 1 to the default number."
Then you store the hashed password in your db as passwordFromLocalDB and to test an incoming password like this:
if (BCrypt.Net.BCrypt.Verify(password, passwordFromLocalDB) == true)
Good Luck!
EDIT: As pointed out by #MikeT, the original link and code I posted is no longer considered best practice for hashing passwords to be stored in a datastore.
This article by Scott Brady illustrates how to use PasswordHasher, and also how to use the interface to develop stronger hashing implementations.
So applying the byte-level hashing function yourself is not required any more, but it you want to see how it's done this article explores the PasswordHasher<T> implementation:
Exploring the ASP.NET Core Identity PasswordHasher
A relevant piece of code from the article is shown below:
private static byte[] HashPasswordV2(string password, RandomNumberGenerator rng)
{
const KeyDerivationPrf Pbkdf2Prf = KeyDerivationPrf.HMACSHA1; // default for Rfc2898DeriveBytes
const int Pbkdf2IterCount = 1000; // default for Rfc2898DeriveBytes
const int Pbkdf2SubkeyLength = 256 / 8; // 256 bits
const int SaltSize = 128 / 8; // 128 bits
// Produce a version 2 text hash.
byte[] salt = new byte[SaltSize];
rng.GetBytes(salt);
byte[] subkey = KeyDerivation.Pbkdf2(password, salt, Pbkdf2Prf, Pbkdf2IterCount, Pbkdf2SubkeyLength);
var outputBytes = new byte[1 + SaltSize + Pbkdf2SubkeyLength];
outputBytes[0] = 0x00; // format marker
Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize);
Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, Pbkdf2SubkeyLength);
return outputBytes;
}
Original answer:
Microsoft has a page up with sample code using PBKDF2 for anyone using .Net Core:
Hash passwords in ASP.NET Core
From the article:
using System;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
public class Program
{
public static void Main(string[] args)
{
Console.Write("Enter a password: ");
string password = Console.ReadLine();
// generate a 128-bit salt using a secure PRNG
byte[] salt = new byte[128 / 8];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
Console.WriteLine($"Salt: {Convert.ToBase64String(salt)}");
// derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password,
salt: salt,
prf: KeyDerivationPrf.HMACSHA1,
iterationCount: 10000,
numBytesRequested: 256 / 8));
Console.WriteLine($"Hashed: {hashed}");
}
}
/*
* SAMPLE OUTPUT
*
* Enter a password: Xtw9NMgx
* Salt: NZsP6NnmfBuYeJrrAKNuVQ==
* Hashed: /OOoOer10+tGwTRDTrQSoeCxVTFr6dtYly7d0cPxIak=
*/
Earlier this year I was looking into the same thing for creating hashes for our ASP.NET Web Forms project, I wanted to do it the same way MVC projects do it out of the box.
I stumbled upon this question => ASP.NET Identity default Password Hasher, how does it work and is it secure?
Then I found the source with the ByteArraysEqual method here => http://www.symbolsource.org/MyGet/Metadata/aspnetwebstacknightly/Project/Microsoft.AspNet.Identity.Core/2.0.0-rtm-140327/Release/Default/Microsoft.AspNet.Identity.Core/Microsoft.AspNet.Identity.Core/Crypto.cs?ImageName=Microsoft.AspNet.Identity.Core
i was interested in an answers that didn't involve any libraries.
I read this article https://crackstation.net/hashing-security.htm which links an implementation in different languages C# among them which i will link here too
https://github.com/defuse/password-hashing/blob/master/PasswordStorage.cs
interestingly it uses Rfc2898DeriveBytes as mentioned a few times here.
private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes){
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt)) {
pbkdf2.IterationCount = iterations;
return pbkdf2.GetBytes(outputBytes);
}
}
For PBKDF2, you might be able to use System.Security.Cryptography.Rfc2898DeriveBytes.
See MSDN here: http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx
PBKDF2 uses HMACSHA1, if you would like a more modern and customisable solution you should look at this API using HMACSHA256 or 512 with key stretching just like PBKDF2
https://sourceforge.net/projects/pwdtknet/
Sample GUI included in source code demonstrated how to get a hash from a password including the creation of crypto random salt.....enjoy :)
PBKDF2
In the example in http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx, when you get to the line "Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(pwd1, salt1,
myIterations);", k1 is the hash. The reason the example is for encryption is that Rfc2898DeriveBytes was originally designed to create encryption keys.
If you do not provide a salt, Rfc2898DeriveBytes will create it's own, but I do not know whether RNGCryptoServiceProvider does a better job of being cryptographically random.
According to OWASP (https://www.owasp.org/index.php/Using_Rfc2898DeriveBytes_for_PBKDF2), the underlying use of SHA1 by Rfc2898DeriveBytes means it's only good for hashes up to 160 bits in length. If you create a longer hash, an attacker still only has to worry about the first 160 bits, but you have made password hashing/authentication more expensive for yourself with no gain.
Here's some example code for Rfc2898DeriveBytes password hashing (store the hash, salt and iterations in the DB):
public class Rfc2898PasswordEncoder
{
private int _byteLength = 160 / 8; // 160 bit hash length
public class EncodedPassword
{
public byte[] Hash { get; set; }
public byte[] Salt { get; set; }
public int Iterations { get; set; }
}
public EncodedPassword EncodePassword(string password, int iterations)
{
var populatedPassword = new EncodedPassword
{
Salt = CreateSalt(),
Iterations = iterations
};
// Add Hash
populatedPassword.Hash = CreateHash(password, populatedPassword.Salt, iterations);
return populatedPassword;
}
public bool ValidatePassword(string password, EncodedPassword encodedPassword)
{
// Create Hash
var testHash = CreateHash(password, encodedPassword.Salt, encodedPassword.Iterations);
return testHash == encodedPassword.Hash;
}
public byte[] CreateSalt()
{
var salt = new byte[_byteLength]; // Salt should be same length as hash
using (var saltGenerator = new RNGCryptoServiceProvider())
{
saltGenerator.GetBytes(salt);
}
return salt;
}
private byte[] CreateHash(string password, byte[] salt, long iterations)
{
byte[] hash;
using (var hashGenerator = new Rfc2898DeriveBytes(password, salt, (int)iterations))
{
hash = hashGenerator.GetBytes(_byteLength);
}
return hash;
}
}
I'm writing a register form for a application but still having problems with being new to c#.
I am looking to encrypt/hash passwords to md5 or sha-256, preferably sha-256.
Any good examples? I want it to be able to take the information from "string password;" and then hash it and store in the variable "string hPassword;". Any ideas?
Don't use a simple hash, or even a salted hash. Use some sort of key-strengthening technique like bcrypt (with a .NET implementation here) or PBKDF2 (with a built-in implementation).
Here's an example using PBKDF2.
To generate a key from your password...
string password = GetPasswordFromUserInput();
// specify that we want to randomly generate a 20-byte salt
using (var deriveBytes = new Rfc2898DeriveBytes(password, 20))
{
byte[] salt = deriveBytes.Salt;
byte[] key = deriveBytes.GetBytes(20); // derive a 20-byte key
// save salt and key to database
}
And then to test if a password is valid...
string password = GetPasswordFromUserInput();
byte[] salt, key;
// load salt and key from database
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt))
{
byte[] newKey = deriveBytes.GetBytes(20); // derive a 20-byte key
if (!newKey.SequenceEqual(key))
throw new InvalidOperationException("Password is invalid!");
}
You're going to want to use the System.Security.Cryptography namespace; specifically, the MD5 class or the SHA256 class.
Drawing a bit from the code on this page, and with the knowledge that both classes have the same base class (HashAlgorithm), you could use a function like this:
public string ComputeHash(string input, HashAlgorithm algorithm)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
Byte[] hashedBytes = algorithm.ComputeHash(inputBytes);
return BitConverter.ToString(hashedBytes);
}
Then you could call it like this (for MD5):
string hPassword = ComputeHash(password, new MD5CryptoServiceProvider());
Or for SHA256:
string hPassword = ComputeHash(password, new SHA256CryptoServiceProvider());
Edit: Adding Salt Support
As dtb pointed out in the comments, this code would be stronger if it included the ability to add salt. If you're not familiar with it, salt is a set of random bits that are included as an input to the hashing function, which goes a long way to thwart dictionary attacks against a hashed password (e.g., using a rainbow table). Here's a modified version of the ComputeHash function that supports salt:
public static string ComputeHash(string input, HashAlgorithm algorithm, Byte[] salt)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
// Combine salt and input bytes
Byte[] saltedInput = new Byte[salt.Length + inputBytes.Length];
salt.CopyTo(saltedInput, 0);
inputBytes.CopyTo(saltedInput, salt.Length);
Byte[] hashedBytes = algorithm.ComputeHash(saltedInput);
return BitConverter.ToString(hashedBytes);
}
Hope this has been helpful!
You should always salt the password before hashing when storing them in the database.
Recommended database columns:
PasswordSalt : int
PasswordHash : binary(20)
Most posts you find online will talk about ASCII encoding the salt and hash, but that is not needed and only add unneeded computation. Also if you use SHA-1, then the output will only be 20 bytes so your hash field in the database only needs to be 20 bytes in length. I understand your asking about SHA-256, but unless you have a compelling reason, using SHA-1 with a salt value will be sufficient in most business practices. If you insist on SHA-256, then the hash field in the database needs to be 32 bytes in length.
Below are a few functions that will generate the salt, compute the hash and verify the hash against a password.
The salt function below generates a cryptographically strong salt as an Integer from 4 cryptographically created random bytes.
private int GenerateSaltForPassword()
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] saltBytes = new byte[4];
rng.GetNonZeroBytes(saltBytes);
return (((int)saltBytes[0]) << 24) + (((int)saltBytes[1]) << 16) + (((int)saltBytes[2]) << 8) + ((int)saltBytes[3]);
}
The password can then be hashed using the salt with the function below. The salt is concatenated to the password and then the hash is computed.
private byte[] ComputePasswordHash(string password, int salt)
{
byte[] saltBytes = new byte[4];
saltBytes[0] = (byte)(salt >> 24);
saltBytes[1] = (byte)(salt >> 16);
saltBytes[2] = (byte)(salt >> 8);
saltBytes[3] = (byte)(salt);
byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(password);
byte[] preHashed = new byte[saltBytes.Length + passwordBytes.Length];
System.Buffer.BlockCopy(passwordBytes, 0, preHashed, 0, passwordBytes.Length);
System.Buffer.BlockCopy(saltBytes, 0, preHashed, passwordBytes.Length, saltBytes.Length);
SHA1 sha1 = SHA1.Create();
return sha1.ComputeHash(preHashed);
}
Checking the password can be done simply by computing the hash and then comparing it to the expected hash.
private bool IsPasswordValid(string passwordToValidate, int salt, byte[] correctPasswordHash)
{
byte[] hashedPassword = ComputePasswordHash(passwordToValidate, salt);
return hashedPassword.SequenceEqual(correctPasswordHash);
}
TL;DR use Microsoft.AspNetCore.Cryptography.KeyDerivation, implementing PBKDF2 with SHA-512.
The good idea to get started with password hashing is to look at what OWASP guidelines say. The list of recommended algorithms includes Argon2, PBKDF2, scrypt, and bcrypt. All these algorithms can be tuned to adjust the time it takes to hash a password, and, correspondingly, the time to crack it via brute-force. All these algorithms utilize salt to protect from rainbow tables attacks.
Neither of these algorithms is terribly weak, but there are some differences:
bcrypt has been around for almost 20 years, has been widely used and
has withstood the test of time. It is pretty resistant to GPU
attacks, but not to FPGA
Argon2 is the newest addition, being a winner of 2015 Password hashing competition. It has better protection against GPU and FPGA attacks, but is a bit too recent to my liking
I don't know much about scrypt. It has been designed to thwart GPU- and FPGA- accelerated attacks, but I've heard it turned out to be not as strong as originally claimed
PBKDF2 is a family of algorithms parametrized by the different hash
functions. It does not offer a specific protection against GPU or ASIC attacks, especially if a weaker hash function like SHA-1 is used, but it is, however, FIPS-certified if it matters to you, and still acceptable if the number of iterations is large enough.
Based on algorithms alone, I would probably go with bcrypt, PBKDF2 being the least favorable.
However, it's not the full story, because even the best algorithm can be made insecure by a bad implementation. Let's look at what is available for .NET platform:
Bcrypt is available via bcrypt.net. They say the implementation is based on Java jBCrypt. Currently there are 6 contributors and 8 issues (all closed) on github. Overall, it looks good, however, I don't know if anyone has made an audit of the code, and it's hard to tell whether an updated version will be available soon enough if a vulnerability is found. I've heard Stack Overflow moved away from using bcrypt because of such reasons
Probably the best way to use Argon2 is through bindings to the
well-known libsodium library, e.g.
https://github.com/adamcaudill/libsodium-net. The idea is that
the most of the crypto is implemented via libsodium, which has considerable
support, and the 'untested' parts are pretty limited. However, in
cryptography details mean a lot, so combined with Argon2 being
relatively recent, I'd treat it as an experimental option
For a long time, .NET had a built-in an implementation of PBKDF2 via
Rfc2898DeriveBytes class. However, the implementation can only use SHA-1 hash function, which is deemed too fast to be secure nowadays
Finally, the most recent solution is
Microsoft.AspNetCore.Cryptography.KeyDerivation package
available via NuGet. It provides PBKDF2 algorithm with SHA-1, SHA-256, or SHA-512 hash functions, which is considerably better than Rfc2898DeriveBytes. The biggest advantage here is that the implementation is supplied by Microsoft, and while I cannot properly assess cryptographic diligence of Microsoft developers versus BCrypt.net or libsodium developers, it just makes sense to trust it because if you are running a .NET application, you are heavily relying on Microsoft already. We might also expect Microsoft to release updates if security issues are found. Hopefully.
To summarize the research up to this point, while PBKDF2 might be the least preferred algorithm of the four, the availability of Microsoft-supplied implementation trumps that, so the reasonable decision would be to use Microsoft.AspNetCore.Cryptography.KeyDerivation.
The recent package at the moment targets .NET Standard 2.0, so available in .NET Core 2.0 or .NET Framework 4.6.1 or later. If you use earlier framework version, it is possible to use the previous version of the package, 1.1.3, which targets .NET Framework 4.5.1 or .NET Core 1.0. Unfortunately, it is not possible to use it in even earlier versions of .NET.
The documentation and the working example is available at learn.microsoft.com. However, do not copy-paste it as it is, there are still decisions a developer needs to make.
The first decision is what hash function to use. Available options include SHA-1, SHA-256, and SHA-512. Of those, SHA-1 is definitely too fast to be secure, SHA-256 is decent, but I would recommend SHA-512, because supposedly, its 64-bit operations usage makes it harder to benefit from GPU-based attacks.
Then, you need to choose the password hash output length and the salt length. It doesn't make sense to have output longer than the hash function output (e.g. 512 bits for SHA-512), and it would probably be the most secure to have it exactly like that. For the salt length, opinions differ. 128 bits should be enough, but in any case, the length longer than the hash output length surely doesn't provide any benefits.
Next, there is an iteration count. The bigger it is, the harder password hashes are to crack, but the longer it takes to log users in. I'd suggest to choose it so the hashing takes 0.25 - 1 seconds on the typical production system, and in any case, it should not be less than 10000.
Normally, you would get bytes array as salt and hash values. Use Base64 to convert them to strings. You can opt to use two different columns in the database, or combine salt and password in one column using a separator which is not encountered in Base64.
Don't forget to devise a password hashing storage in a way that allows to seamlessly move to a better hashing algorithm in future.
If you are going to be storing the hashed passwords, use bcrypt instead of SHA-256. The problem is that SHA-256 is optimized for speed, which makes it easier for a brute force attack on passwords should someone get access to your database.
Read this article: Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes and this answer to a previous SO question.
Some quotes from the article:
The problem is that MD5 is fast. So are its modern competitors, like SHA1 and SHA256. Speed is a design goal of a modern secure hash, because hashes are a building block of almost every cryptosystem, and usually get demand-executed on a per-packet or per-message basis.
Speed is exactly what you don’t want in a password hash function.
Finally, we learned that if we want to store passwords securely we have three reasonable options: PHK’s MD5 scheme, Provos-Maziere’s Bcrypt scheme, and SRP. We learned that the correct choice is Bcrypt.
PBKDF2 is using HMACSHA1.......if you want more modern HMACSHA256 or HMACSHA512 implementation and still want key stretching to make the algorithm slower I suggest this API: https://sourceforge.net/projects/pwdtknet/
Here is a full implementation of a persistence unaware SecuredPassword class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
public class SecuredPassword
{
private const int saltSize = 256;
private readonly byte[] hash;
private readonly byte[] salt;
public byte[] Hash
{
get { return hash; }
}
public byte[] Salt
{
get { return salt; }
}
public SecuredPassword(string plainPassword)
{
if (string.IsNullOrWhiteSpace(plainPassword))
return;
using (var deriveBytes = new Rfc2898DeriveBytes(plainPassword, saltSize))
{
salt = deriveBytes.Salt;
hash = deriveBytes.GetBytes(saltSize);
}
}
public SecuredPassword(byte[] hash, byte[] salt)
{
this.hash = hash;
this.salt = salt;
}
public bool Verify(string password)
{
if (string.IsNullOrWhiteSpace(password))
return false;
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt))
{
byte[] newKey = deriveBytes.GetBytes(saltSize);
return newKey.SequenceEqual(hash);
}
}
}
And tests:
public class SecuredPasswordTests
{
[Test]
public void IsHashed_AsExpected()
{
var securedPassword = new SecuredPassword("password");
Assert.That(securedPassword.Hash, Is.Not.EqualTo("password"));
Assert.That(securedPassword.Hash.Length, Is.EqualTo(256));
}
[Test]
public void Generates_Unique_Salt()
{
var securedPassword = new SecuredPassword("password");
var securedPassword2 = new SecuredPassword("password");
Assert.That(securedPassword.Salt, Is.Not.Null);
Assert.That(securedPassword2.Salt, Is.Not.Null);
Assert.That(securedPassword.Salt, Is.Not.EqualTo(securedPassword2.Salt));
}
[Test]
public void Generates_Unique_Hash()
{
var securedPassword = new SecuredPassword("password");
var securedPassword2 = new SecuredPassword("password");
Assert.That(securedPassword.Hash, Is.Not.Null);
Assert.That(securedPassword2.Hash, Is.Not.Null);
Assert.That(securedPassword.Hash, Is.Not.EqualTo(securedPassword2.Hash));
}
[Test]
public void Verify_WhenMatching_ReturnsTrue()
{
var securedPassword = new SecuredPassword("password");
var result = securedPassword.Verify("password");
Assert.That(result, Is.True);
}
[Test]
public void Verify_WhenDifferent_ReturnsFalse()
{
var securedPassword = new SecuredPassword("password");
var result = securedPassword.Verify("Password");
Assert.That(result, Is.False);
}
[Test]
public void Verify_WhenRehydrated_AndMatching_ReturnsTrue()
{
var securedPassword = new SecuredPassword("password123");
var rehydrated = new SecuredPassword(securedPassword.Hash, securedPassword.Salt);
var result = rehydrated.Verify("password123");
Assert.That(result, Is.True);
}
[Test]
public void Constructor_Handles_Null_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword(null));
}
[Test]
public void Constructor_Handles_Empty_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword(string.Empty));
}
[Test]
public void Verify_Handles_Null_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword("password").Verify(null));
}
[Test]
public void Verify_Handles_Empty_Password()
{
Assert.DoesNotThrow(() => new SecuredPassword("password").Verify(string.Empty));
}
[Test]
public void Verify_When_Null_Password_ReturnsFalse()
{
Assert.That(new SecuredPassword("password").Verify(null), Is.False);
}
}
The System.Security.Cryptography.SHA256 class should do the trick:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.sha256.aspx
Please use this as i have the same issues before but could solve it will the litle code snippet
public static string ComputeHash(string input, HashAlgorithm algorithm, Byte[] salt)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
// Combine salt and input bytes
Byte[] saltedInput = new Byte[salt.Length + inputBytes.Length];
salt.CopyTo(saltedInput, 0);
inputBytes.CopyTo(saltedInput, salt.Length);
Byte[] hashedBytes = algorithm.ComputeHash(saltedInput);
StringBuilder hex = new StringBuilder(hashedBytes.Length * 2);
foreach (byte b in hashedBytes)
hex.AppendFormat("{0:X2}", b);
return hex.ToString();
}