Hash and salt passwords in C#
Asked Answered
S

17

208

I was just going through one of DavidHayden's articles on Hashing User Passwords.

Really I can't get what he is trying to achieve.

Here is his code:

private static string CreateSalt(int size)
{
    //Generate a cryptographic random number.
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    byte[] buff = new byte[size];
    rng.GetBytes(buff);

    // Return a Base64 string representation of the random number.
    return Convert.ToBase64String(buff);
}

private static string CreatePasswordHash(string pwd, string salt)
{
    string saltAndPwd = String.Concat(pwd, salt);
    string hashedPwd =
        FormsAuthentication.HashPasswordForStoringInConfigFile(
        saltAndPwd, "sha1");
    return hashedPwd;
}

Is there any other C# method for hashing passwords and adding salt to it?

Scarify answered 26/1, 2010 at 9:20 Comment(6)
here is a library that does the hashing with salt encrypto.codeplex.comGujarati
What should you pass in for the size in the first method to generate salt?Maurizio
Link is broken.Sanctity
@ShaneLeBlanc You should at least as many bits as the has function outputs. SHA1 is not crypto-grade, so you should at least use SHA256, which outputs 256 bits or 32 bytes. BUT, 256 bits is NOT easily convertible to base 64, because each base64 char encodes 6 bits, and 256 is not divisible wholly by 6. So you need a common denominator of 6 (for base64) and 8 (for bits in a byte) over 256 bits, which is 264 bites or 33 bytes. TLDR: Use 33.Coincide
^ source: crackstation.net/hashing-security.htmCoincide
Please note that the RNGCryptoServiceProvider is Disposable. So for the security you should dispose it after.Reaction
A
266

Actually this is kind of strange, with the string conversions - which the membership provider does to put them into config files. Hashes and salts are binary blobs, you don't need to convert them to strings unless you want to put them into text files.

In my book, Beginning ASP.NET Security, (oh finally, an excuse to pimp the book) I do the following

static byte[] GenerateSaltedHash(byte[] plainText, byte[] salt)
{
  HashAlgorithm algorithm = new SHA256Managed();

  byte[] plainTextWithSaltBytes = 
    new byte[plainText.Length + salt.Length];

  for (int i = 0; i < plainText.Length; i++)
  {
    plainTextWithSaltBytes[i] = plainText[i];
  }
  for (int i = 0; i < salt.Length; i++)
  {
    plainTextWithSaltBytes[plainText.Length + i] = salt[i];
  }

  return algorithm.ComputeHash(plainTextWithSaltBytes);            
}

The salt generation is as the example in the question. You can convert text to byte arrays using Encoding.UTF8.GetBytes(string). If you must convert a hash to its string representation you can use Convert.ToBase64String and Convert.FromBase64String to convert it back.

You should note that you cannot use the equality operator on byte arrays, it checks references and so you should simply loop through both arrays checking each byte thus

public static bool CompareByteArrays(byte[] array1, byte[] array2)
{
  if (array1.Length != array2.Length)
  {
    return false;
  }

  for (int i = 0; i < array1.Length; i++)
  {
    if (array1[i] != array2[i])
    {
      return false;
    }
  }

  return true;
}

Always use a new salt per password. Salts do not have to be kept secret and can be stored alongside the hash itself.

Afterburning answered 26/1, 2010 at 9:49 Comment(20)
Thanks for this advice - really helped get me started. I also came across this link < dijksterhuis.org/creating-salted-hash-values-in-c > which I found was good practical advice and mirrors much of what was said in this postOlivine
nifty LINQ statement refactor for CompareByteArrays return array1.Length == array2.Length && !array1.Where((t, i) => t != array2[i]).Any();Hyposensitize
Could be a bit more efficient and shorter by using 'Array.Copy' to copy plain text and salt into the new array.Liquor
Most of CompareByteArrays can be replaced by array1.SequenceEqual(array2); Just make sure to keep the length check for speed (if you care). Also the plainTextWithSaltBytes generation can be replaced with byte[] plainTextWithSaltBytes = plainText.Concat(salt).ToArray();Shoveler
You say that the salts don't need to be kept secret, but isn't easier to crack the hash if you have the salt?Brilliancy
@Brilliancy Technically, yes, but having a unique salt for each user renders Rainbow Tables (generally accepted as the most efficient way to crack hashed passwords) practically useless. This is a quick oveview gives a in-depth but not overwhelming overview of how to store passwords securely, and why/how it all works.Dosi
@Hyposensitize : you should add a .ToList() to make it constant time. e.g.: return array1.Length == array2.Length && !array1.Where((t, i) => t != array2[i]).ToList().Any(); Else, LINQ will return as soon as it finds one byte that isn't equal.Wellknown
You can make GenerateSaltedHash() 6 lines shorter by using Array.Copy() Array.Copy(plainText, plainTextWithSaltBytes, plainText.Length); Array.Copy(salt, 0, plainTextWithSaltBytes, plainText.Length, salt.Length); (Of course, I would have chosen shorter variable names.)Atalayah
-1 for using a fast hash-function. Use a slow construction like PBKDF2, bcrypt or scrypt.Barden
It's also considered good practice to use a constant time comparison, but that doesn't matter much with password hashes.Barden
@Brettski, if you're concerned about storing the plain salt in the database, you could always use reversible encryption before storing it, and use a static salt for that.Seriocomic
For more on comparing byte arrays please see #43789 especially Hafthor's answer.Sobriety
As pointed out by @CodesInChaos, the above code, while taking into accounts rainbow tables fails to provide protection against dictionary based attacks. This is not what good password hashing code looks like. Please research KDFs (Key Derivation Functions).Acariasis
Do not use SHA256 or any other general hashing algorithm for storing passwords. They are far too fast, allowing for brute force attacks. Instead, use PBKDF2, bcrypt, or scrypt, which have work factors to intentionally slow them down. See https://mcmap.net/q/129019/-how-to-hash-a-passwordEhrlich
@AlexRouillard Adding .ToList() will not make it "length-constant" time. You only transform it to bool array and .Any() will return true as soon as it finds one true value in array. In this article is explanation why we should use "time constant" compassion and how to implement it.Derangement
This is very correct what the others say that KDF should be used, please check my answer below (https://mcmap.net/q/126442/-hash-and-salt-passwords-in-c) for a very simple library that does the task based on microsoft cryptoClaviform
Care the CompareByteArrays function is vulnerable to timing attacks..Nicodemus
new SHA256Managed(0) is considered obsolete. Now it's just HashAlgorithm algorithm = SHA256.Create();At least I think, still have yet to test this. At least IDE doesn't throw any errors now.Izawa
SHA256Managed is not disposed.Reaction
Hackers can see this post and will find ways to circumvent the salts.Knurly
F
54

What blowdart said, but with a little less code. Use Linq or CopyTo to concatenate arrays.

public static byte[] Hash(string value, byte[] salt)
{
    return Hash(Encoding.UTF8.GetBytes(value), salt);
}

public static byte[] Hash(byte[] value, byte[] salt)
{
    byte[] saltedValue = value.Concat(salt).ToArray();
    // Alternatively use CopyTo.
    //var saltedValue = new byte[value.Length + salt.Length];
    //value.CopyTo(saltedValue, 0);
    //salt.CopyTo(saltedValue, value.Length);

    return new SHA256Managed().ComputeHash(saltedValue);
}

Linq has an easy way to compare your byte arrays too.

public bool ConfirmPassword(string password)
{
    byte[] passwordHash = Hash(password, _passwordSalt);

    return _passwordHash.SequenceEqual(passwordHash);
}

Before implementing any of this however, check out this post. For password hashing you may want a slow hash algorithm, not a fast one.

To that end there is the Rfc2898DeriveBytes class which is slow (and can be made slower), and may answer the second part of the original question in that it can take a password and salt and return a hash. See this question for more information. Note, Stack Exchange is using Rfc2898DeriveBytes for password hashing (source code here).

Forecourse answered 5/1, 2012 at 22:5 Comment(1)
@MushinNoShin SHA256 is a fast hash. Password hashing needs a slow hash, like PBKDF2, bcrypt or scrypt. See How to securely hash passwords? on security.se for details.Barden
L
39

I've been reading that hashing functions like SHA256 weren't really intended for use with storing passwords: https://patrickmn.com/security/storing-passwords-securely/#notpasswordhashes

Instead adaptive key derivation functions like PBKDF2, bcrypt or scrypt were. Here is a PBKDF2 based one that Microsoft wrote for PasswordHasher in their Microsoft.AspNet.Identity library:

/* =======================
 * HASHED PASSWORD FORMATS
 * =======================
 * 
 * Version 3:
 * PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
 * Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
 * (All UInt32s are stored big-endian.)
 */

public string HashPassword(string password)
{
    var prf = KeyDerivationPrf.HMACSHA256;
    var rng = RandomNumberGenerator.Create();
    const int iterCount = 10000;
    const int saltSize = 128 / 8;
    const int numBytesRequested = 256 / 8;

    // Produce a version 3 (see comment above) text hash.
    var salt = new byte[saltSize];
    rng.GetBytes(salt);
    var 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, iterCount);
    WriteNetworkByteOrder(outputBytes, 9, saltSize);
    Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
    Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
    return Convert.ToBase64String(outputBytes);
}

public bool VerifyHashedPassword(string hashedPassword, string providedPassword)
{
    var decodedHashedPassword = Convert.FromBase64String(hashedPassword);

    // Wrong version
    if (decodedHashedPassword[0] != 0x01)
        return false;

    // Read header information
    var prf = (KeyDerivationPrf)ReadNetworkByteOrder(decodedHashedPassword, 1);
    var iterCount = (int)ReadNetworkByteOrder(decodedHashedPassword, 5);
    var saltLength = (int)ReadNetworkByteOrder(decodedHashedPassword, 9);

    // Read the salt: must be >= 128 bits
    if (saltLength < 128 / 8)
    {
        return false;
    }
    var salt = new byte[saltLength];
    Buffer.BlockCopy(decodedHashedPassword, 13, salt, 0, salt.Length);

    // Read the subkey (the rest of the payload): must be >= 128 bits
    var subkeyLength = decodedHashedPassword.Length - 13 - salt.Length;
    if (subkeyLength < 128 / 8)
    {
        return false;
    }
    var expectedSubkey = new byte[subkeyLength];
    Buffer.BlockCopy(decodedHashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length);

    // Hash the incoming password and verify it
    var actualSubkey = KeyDerivation.Pbkdf2(providedPassword, salt, prf, iterCount, subkeyLength);
    return actualSubkey.SequenceEqual(expectedSubkey);
}

private static void WriteNetworkByteOrder(byte[] buffer, int offset, uint value)
{
    buffer[offset + 0] = (byte)(value >> 24);
    buffer[offset + 1] = (byte)(value >> 16);
    buffer[offset + 2] = (byte)(value >> 8);
    buffer[offset + 3] = (byte)(value >> 0);
}

private static uint ReadNetworkByteOrder(byte[] buffer, int offset)
{
    return ((uint)(buffer[offset + 0]) << 24)
        | ((uint)(buffer[offset + 1]) << 16)
        | ((uint)(buffer[offset + 2]) << 8)
        | ((uint)(buffer[offset + 3]));
}

Note this requires Microsoft.AspNetCore.Cryptography.KeyDerivation nuget package installed which requires .NET Standard 2.0 (.NET 4.6.1 or higher). For earlier versions of .NET see the Crypto class from Microsoft's System.Web.Helpers library.

Update Nov 2015
Updated answer to use an implementation from a different Microsoft library which uses PBKDF2-HMAC-SHA256 hashing instead of PBKDF2-HMAC-SHA1 (note PBKDF2-HMAC-SHA1 is still secure if iterCount is high enough). You can check out the source the simplified code was copied from as it actually handles validating and upgrading hashes implemented from previous answer, useful if you need to increase iterCount in the future.

Lawanda answered 31/5, 2013 at 12:54 Comment(6)
Note it maybe worth increasing PBKDF2IterCount to a higher number, see security.stackexchange.com/q/3959 for more.Lawanda
1) Reduce PBKDF2SubkeyLength to 20 bytes. That's the natural size f SHA1 and increasing it beyond that slows down the defender without slowing down the attacker. 2) I recommend increasing the iteration count. I recommend 10k to 100k depending on your performance budget. 3) A constant time comparison wouldn't hurt either, but doesn't have much practical impact.Barden
KeyDerivationPrf, KeyDerivation and BlockCopy is undefined, what is their classes?Cystic
@mrbengi Have you installed the Microsoft.AspNet.Cryptography.KeyDerivation nuget package mentioned? If that's not suitable here is a version that doesn't require the nuget package. Buffer.BlockCopy should exist it's part of System.Lawanda
The nuget package is now Microsoft.AspNetCore.Cryptography.KeyDerivation.Tempietempla
I have made a library to make it easy to work with Microsoft cryptography: https://mcmap.net/q/126442/-hash-and-salt-passwords-in-cClaviform
V
27

Salt is used to add an extra level of complexity to the hash, to make it harder to brute-force crack.

From an article on Sitepoint:

A hacker can still perform what's called a dictionary attack. Malicious parties may make a dictionary attack by taking, for instance, 100,000 passwords that they know people use frequently (e.g. city names, sports teams, etc.), hash them, and then compare each entry in the dictionary against each row in the database table. If the hackers find a match, bingo! They have your password. To solve this problem, however, we need only salt the hash.

To salt a hash, we simply come up with a random-looking string of text, concatenate it with the password supplied by the user, then hash both the randomly generated string and password together as one value. We then save both the hash and the salt as separate fields within the Users table.

In this scenario, not only would a hacker need to guess the password, they'd have to guess the salt as well. Adding salt to the clear text improves security: now, if a hacker tries a dictionary attack, he must hash his 100,000 entries with the salt of every user row. Although it's still possible, the chances of hacking success diminish radically.

There is no method automatically doing this in .NET, so you'll have go with the solution above.

Volcanology answered 26/1, 2010 at 9:30 Comment(1)
Salts are used to defend against things like rainbow tables. To defend against dictionary attacks a work factor (also known as key stretching) is required like any good KDF: en.wikipedia.org/wiki/Key_stretchingAcariasis
E
21

2022 (.NET 6+) solution:

Most of the other answers here (including the accepted answer) are using the SHA-256 hashing algorithm, which is NOT suited for storing user passwords anymore, even if you use salts. You instead should opt for slower hashing functions for this purpose, such as Bcrypt, Argon2, Scrypt, or PBKDF2; the latter being the only one that's natively available in .NET.

You can find helper methods and whatnot to create PBKDF2 hashes primarily in this other question, but the one I'm providing below has the following advantages over those provided in that question or even some here, such as this one.

Pros:

  • Uses the new static Rfc2898DeriveBytes.Pbkdf2() method introduced in .NET 6, eliminating the need to instantiate and also dispose the object every single time.

  • Uses the new RandomNumberGenerator class and its static GetBytes method — introduced in .NET 6 — to generate the salt. The RNGCryptoServiceProvider class used in the original question and many of the answers here is obsolete.

  • Uses the CryptographicOperations.FixedTimeEquals method (introduced in .NET Core 2.1) for comparing the key bytes in the Verify method, instead of doing the comparison by hand — like the accepted answer is doing. This, in addition to removing a lot of noisy boilerplate, also nullifies timing attacks.

  • Uses SHA-256 instead of the default SHA-1 as the underlying algorithm, just to be on the safe side, as the latter is a more robust and reliable algorithm.

  • The string that the Hash method returns (and by extension the string that the Verify method receives) has the following structure:

    [key]:[salt]:[iterations]:[algorithm]

    This is the most important advantage of this particular solution; this means that we're basically including metadata about the configurations used to create the hash in the final string. This effectively allows us to change the settings (such as the number of iterations, salt/key size, etc.) in our hasher class in the future, without breaking previous hashes created with the old settings. This is something that most of the other solutions I've come across (that are using PBKDF2) actually neglect and fail to take into account, even though it's crucial. They instead typically rely on the current configuration values to verify hashes, which means that as soon as you decide to change any of the configuration values, any previously-created hashes will no longer be verified properly.

Other points:

  • I'm using the hexadecimal representation of the key and the salt in the returned hash string. You can instead use base64 if you prefer, simply by changing every occurrence of Convert.ToHexString and Convert.FromHexString to Convert.ToBase64 and Convert.FromBase64 respectively. The rest of the logic remains exactly the same.
  • The often recommended salt size is 64 bits or above. I've set it to 128 bits.
  • The key size should normally be the same as the natural output size of your chosen algorithm — see this comment. In our case, as I mentioned earlier, the underlying algorithm is SHA-256, whose output size is 256 bits, which is precisely what we're setting our key size to.
  • If you plan to use this for storing user passwords, it's usually recommended to use at least 10,000 iterations or more. I've set the default value to 50,000, which you can of course change as you see fit.

The code:

public static class SecretHasher
{
    private const int _saltSize = 16; // 128 bits
    private const int _keySize = 32; // 256 bits
    private const int _iterations = 50000;
    private static readonly HashAlgorithmName _algorithm = HashAlgorithmName.SHA256;

    private const char segmentDelimiter = ':';

    public static string Hash(string input)
    {
        byte[] salt = RandomNumberGenerator.GetBytes(_saltSize);
        byte[] hash = Rfc2898DeriveBytes.Pbkdf2(
            input,
            salt,
            _iterations,
            _algorithm,
            _keySize
        );
        return string.Join(
            segmentDelimiter,
            Convert.ToHexString(hash),
            Convert.ToHexString(salt),
            _iterations,
            _algorithm
        );
    }

    public static bool Verify(string input, string hashString)
    {
        string[] segments = hashString.Split(segmentDelimiter);
        byte[] hash = Convert.FromHexString(segments[0]);
        byte[] salt = Convert.FromHexString(segments[1]);
        int iterations = int.Parse(segments[2]);
        HashAlgorithmName algorithm = new HashAlgorithmName(segments[3]);
        byte[] inputHash = Rfc2898DeriveBytes.Pbkdf2(
            input,
            salt,
            iterations,
            algorithm,
            hash.Length
        );
        return CryptographicOperations.FixedTimeEquals(inputHash, hash);
    }
}

Usage:

// Hash:
string password = "...";
string hashed = SecretHasher.Hash(password);

// Verify:
string enteredPassword = "...";
bool isPasswordCorrect = SecretHasher.Verify(enteredPassword, hashed);
Expellee answered 26/7, 2022 at 15:59 Comment(0)
H
20

I created a class that has the following method:

  1. Create Salt

  2. Hash Input

  3. Validate input

    public class CryptographyProcessor
    {
        public string CreateSalt(int size)
        {
            //Generate a cryptographic random number.
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            byte[] buff = new byte[size];
            rng.GetBytes(buff);
            return Convert.ToBase64String(buff);
        }
    
        public string GenerateHash(string input, string salt)
        { 
            byte[] bytes = Encoding.UTF8.GetBytes(input + salt);
            SHA256Managed sHA256ManagedString = new SHA256Managed();
            byte[] hash = sHA256ManagedString.ComputeHash(bytes);
            return Convert.ToBase64String(hash);
        }
    
        public bool AreEqual(string plainTextInput, string hashedInput, string salt)
        {
            string newHashedPin = GenerateHash(plainTextInput, salt);
            return newHashedPin.Equals(hashedInput); 
        }
    }
    
Haskins answered 17/7, 2018 at 10:8 Comment(2)
AlegbeI tried it, but it generate two different has values for hte same input.Pamalapamela
A single iteration of SHA256 is NOT good enough for storing passwords, see this answer.Expellee
L
11

Use the System.Web.Helpers.Crypto NuGet package from Microsoft. It automatically adds salt to the hash.

You hash a password like this: var hash = Crypto.HashPassword("foo");

You verify a password like this: var verified = Crypto.VerifyHashedPassword(hash, "foo");

Labroid answered 23/7, 2019 at 9:22 Comment(0)
C
7

I have made a library SimpleHashing.Net to make the process of hashing easy with basic classes provided by Microsoft. Ordinary SHA is not really enough to have passwords stored securely anymore.

The library use the idea of hash format from Bcrypt, but since there is no official MS implementation I prefer to use what's available in the framework (i.e. PBKDF2), but it's a bit too hard out of the box.

This is a quick example how to use the library:

ISimpleHash simpleHash = new SimpleHash();

// Creating a user hash, hashedPassword can be stored in a database
// hashedPassword contains the number of iterations and salt inside it similar to bcrypt format
string hashedPassword = simpleHash.Compute("Password123");

// Validating user's password by first loading it from database by username
string storedHash = _repository.GetUserPasswordHash(username);
isPasswordValid = simpleHash.Verify("Password123", storedHash);
Claviform answered 18/11, 2016 at 19:41 Comment(0)
T
5

Bah, this is better! http://sourceforge.net/projects/pwdtknet/ and it is better because ..... it performs Key Stretching AND uses HMACSHA512 :)

Tsar answered 23/8, 2013 at 10:2 Comment(0)
T
4

If you dont use asp.net or .net core there is also an easy way in >= .Net Standard 2.0 projects.

First you can set the desired size of the hash, salt and iteration number which is related to the duration of the hash generation:

private const int SaltSize = 32;
private const int HashSize = 32;
private const int IterationCount = 10000;

To generare the password hash and salt you can use something like this:

public static string GeneratePasswordHash(string password, out string salt)
{
    using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, SaltSize))
    {
        rfc2898DeriveBytes.IterationCount = IterationCount;
        byte[] hashData = rfc2898DeriveBytes.GetBytes(HashSize);
        byte[] saltData = rfc2898DeriveBytes.Salt;
        salt = Convert.ToBase64String(saltData);
        return Convert.ToBase64String(hashData);
    }
}

To verify if the password which the user entered is valid you can check with the values in your database:

public static bool VerifyPassword(string password, string passwordHash, string salt)
{
    using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, SaltSize))
    {
        rfc2898DeriveBytes.IterationCount = IterationCount;
        rfc2898DeriveBytes.Salt = Convert.FromBase64String(salt);
        byte[] hashData = rfc2898DeriveBytes.GetBytes(HashSize);
        return Convert.ToBase64String(hashData) == passwordHash;
    }
}

The following unit test shows the usage:

string password = "MySecret";

string passwordHash = PasswordHasher.GeneratePasswordHash(password, out string salt);

Assert.True(PasswordHasher.VerifyPassword(password, passwordHash, salt));
Assert.False(PasswordHasher.VerifyPassword(password.ToUpper(), passwordHash, salt));

Microsoft Rfc2898DeriveBytes Source

Trolly answered 14/1, 2020 at 19:39 Comment(0)
C
2

This is how I do it.. I create the hash and store it using the ProtectedData api:

    public static string GenerateKeyHash(string Password)
    {
        if (string.IsNullOrEmpty(Password)) return null;
        if (Password.Length < 1) return null;

        byte[] salt = new byte[20];
        byte[] key = new byte[20];
        byte[] ret = new byte[40];

        try
        {
            using (RNGCryptoServiceProvider randomBytes = new RNGCryptoServiceProvider())
            {
                randomBytes.GetBytes(salt);

                using (var hashBytes = new Rfc2898DeriveBytes(Password, salt, 10000))
                {
                    key = hashBytes.GetBytes(20);
                    Buffer.BlockCopy(salt, 0, ret, 0, 20);
                    Buffer.BlockCopy(key, 0, ret, 20, 20);
                }
            }
            // returns salt/key pair
            return Convert.ToBase64String(ret);
        }
        finally
        {
            if (salt != null)
                Array.Clear(salt, 0, salt.Length);
            if (key != null)
                Array.Clear(key, 0, key.Length);
            if (ret != null)
                Array.Clear(ret, 0, ret.Length);
        } 
    }

    public static bool ComparePasswords(string PasswordHash, string Password)
    {
        if (string.IsNullOrEmpty(PasswordHash) || string.IsNullOrEmpty(Password)) return false;
        if (PasswordHash.Length < 40 || Password.Length < 1) return false;

        byte[] salt = new byte[20];
        byte[] key = new byte[20];
        byte[] hash = Convert.FromBase64String(PasswordHash);

        try
        {
            Buffer.BlockCopy(hash, 0, salt, 0, 20);
            Buffer.BlockCopy(hash, 20, key, 0, 20);

            using (var hashBytes = new Rfc2898DeriveBytes(Password, salt, 10000))
            {
                byte[] newKey = hashBytes.GetBytes(20);

                if (newKey != null)
                    if (newKey.SequenceEqual(key))
                        return true;
            }
            return false;
        }
        finally
        {
            if (salt != null)
                Array.Clear(salt, 0, salt.Length);
            if (key != null)
                Array.Clear(key, 0, key.Length);
            if (hash != null)
                Array.Clear(hash, 0, hash.Length);
        }
    }

    public static byte[] DecryptData(string Data, byte[] Salt)
    {
        if (string.IsNullOrEmpty(Data)) return null;

        byte[] btData = Convert.FromBase64String(Data);

        try
        {
            return ProtectedData.Unprotect(btData, Salt, DataProtectionScope.CurrentUser);
        }
        finally
        {
            if (btData != null)
                Array.Clear(btData, 0, btData.Length);
        }
    }

    public static string EncryptData(byte[] Data, byte[] Salt)
    {
        if (Data == null) return null;
        if (Data.Length < 1) return null;

        byte[] buffer = new byte[Data.Length];

        try
        {
            Buffer.BlockCopy(Data, 0, buffer, 0, Data.Length);
            return System.Convert.ToBase64String(ProtectedData.Protect(buffer, Salt, DataProtectionScope.CurrentUser));
        }
        finally
        {
            if (buffer != null)
                Array.Clear(buffer, 0, buffer.Length);
        }
    }
Covenantor answered 5/3, 2014 at 20:37 Comment(1)
How to I call it while saving and when comparing later?Saxena
V
2

I read all answers and I think those enough, specially @Michael articles with slow hashing and @CodesInChaos good comments, but I decided to share my code snippet for hashing/validating that may be useful and it does not require [Microsoft.AspNet.Cryptography.KeyDerivation].

    private static bool SlowEquals(byte[] a, byte[] b)
            {
                uint diff = (uint)a.Length ^ (uint)b.Length;
                for (int i = 0; i < a.Length && i < b.Length; i++)
                    diff |= (uint)(a[i] ^ b[i]);
                return diff == 0;
            }

    private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)
            {
                Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt);
                pbkdf2.IterationCount = iterations;
                return pbkdf2.GetBytes(outputBytes);
            }

    private static string CreateHash(string value, int salt_bytes, int hash_bytes, int pbkdf2_iterations)
            {
                // Generate a random salt
                RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
                byte[] salt = new byte[salt_bytes];
                csprng.GetBytes(salt);

                // Hash the value and encode the parameters
                byte[] hash = PBKDF2(value, salt, pbkdf2_iterations, hash_bytes);

                //You need to return the salt value too for the validation process
                return Convert.ToBase64String(hash) + ":" + 
                       Convert.ToBase64String(hash);
            }

    private static bool ValidateHash(string pureVal, string saltVal, string hashVal, int pbkdf2_iterations)
            {
                try
                {
                    byte[] salt = Convert.FromBase64String(saltVal);
                    byte[] hash = Convert.FromBase64String(hashVal);

                    byte[] testHash = PBKDF2(pureVal, salt, pbkdf2_iterations, hash.Length);
                    return SlowEquals(hash, testHash);
                }
                catch (Exception ex)
                {
                    return false;
                }
            }

Please pay attention SlowEquals function that is so important, Finally, I hope this help and Please don't hesitate to advise me about better approaches.

Vishinsky answered 16/6, 2018 at 11:19 Comment(3)
Rather than creating a busy loop, why not put in an artificial non-busy delay. e.g. using Task.Delay. This will delay a brute force attempt but not block the active thread.Severally
@gburton Thanks for your advice. I will check it.Vishinsky
There is a typo in CreateHash: you are concaneting Convert.ToBase64String(hash) to itself instead of the salt. Other than that, this is a nice answer which adresses pretty much every issue raised in comments on other answers.Wearable
C
2

I use this in .netcore6

public class Encryption
{
    public string CreateSalt(int size)
    {
        //Generate a cryptographic random number.
        byte[] buff = new byte[size];
        RandomNumberGenerator rng = RandomNumberGenerator.Create();
        rng.GetBytes(buff);
        return Convert.ToBase64String(buff);
    }

    public string GenerateHash(string input, string salt)
    {
        byte[] bytes = Encoding.UTF8.GetBytes(input + salt);
        SHA256 sha = SHA256.Create();
        byte[] hash = sha.ComputeHash(bytes);
        return Convert.ToBase64String(hash);
    }

    public bool Equals(string plainTextInput, string hashedInput, string salt)
    {
        string newHashedPin = GenerateHash(plainTextInput, salt);
        return newHashedPin.Equals(hashedInput);
    }
}
Coralyn answered 12/5, 2022 at 9:59 Comment(0)
T
0

There are already good answers to the original question, but I would like to add, that the "SequenceEqual" enables the possibility of a timing attack.

The normal way to check for sequences (of bytes) are the same, is to compare every byte in the order with the second one. The first one that is out of order stops the comparison and "false" is returned.

byte[] hash1 = ...
byte[] hash2 = ...
// can be exploited with a timing attack
bool equals = hash1.SequenceEqual(hash2);

With that, an attacker needs 256 strings with every possible starting byte. He runs every string against the mechanism and the one that takes the longest to get a result is the one with the correct first byte. The attack can then be continued in a similar manner on the next byte... and so on.

I found here a better way of doing it, with a nice explanation.

[MethodImpl(MethodImplOptions.NoOptimization)]
private static bool slowEquals(byte[] a, byte[] b)
{
    int diff = a.Length ^ b.Length;

    for (int i = 0; i < a.Length && i < b.Length; i++)
        diff |= a[i] ^ b[i];

    return diff == 0;
}
Treillage answered 12/11, 2021 at 16:25 Comment(1)
The time spent comparing the bytes is an infinitesimal portion of the entire operation. Particularly in all the cases where there is a network request going to fetch the password. And even in the very rare instances where the operation is so consistent that this would even be possible, it would require lots and lots of attempts. Many many thousands. At that point your risk of people guessing and checking is way, way higher than using something like this (hence why you need to rate limit password requests anyway. This just isn't a real concern..Catricecatrina
D
-1

In answer to this part of the original question "Is there any other C# method for hashing passwords" You can achieve this using ASP.NET Identity v3.0 https://www.nuget.org/packages/Microsoft.AspNet.Identity.EntityFramework/3.0.0-rc1-final

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using System.Security.Principal;

namespace HashTest{


    class Program
    {
        static void Main(string[] args)
        {

            WindowsIdentity wi = WindowsIdentity.GetCurrent();

            var ph = new PasswordHasher<WindowsIdentity>();

            Console.WriteLine(ph.HashPassword(wi,"test"));

            Console.WriteLine(ph.VerifyHashedPassword(wi,"AQAAAAEAACcQAAAAEA5S5X7dmbx/NzTk6ixCX+bi8zbKqBUjBhID3Dg1teh+TRZMkAy3CZC5yIfbLqwk2A==","test"));

        }
    }


}
Deplete answered 8/4, 2016 at 8:37 Comment(0)
S
-1
 protected void m_GenerateSHA256_Button1_Click(objectSender, EventArgs e)
{
string salt =createSalt(10);
string hashedPassword=GenerateSHA256Hash(m_UserInput_TextBox.Text,Salt);
m_SaltHash_TextBox.Text=Salt;
 m_SaltSHA256Hash_TextBox.Text=hashedPassword;

}
 public string createSalt(int size)
{
 var rng= new System.Security.Cyptography.RNGCyptoServiceProvider();
 var buff= new byte[size];
rng.GetBytes(buff);
 return Convert.ToBase64String(buff);
}


 public string GenerateSHA256Hash(string input,string salt)
{
 byte[]bytes=System.Text.Encoding.UTF8.GetBytes(input+salt);
 new System.Security.Cyptography.SHA256Managed();
 byte[]hash=sha256hashString.ComputedHash(bytes);
 return bytesArrayToHexString(hash);
  }
Superstratum answered 24/4, 2018 at 18:5 Comment(1)
other method is string password=HashPasswordForStoringInConfigFile(TextBox1.Text,SHA1)Superstratum
I
-7
create proc [dbo].[hash_pass] @family nvarchar(50), @username nvarchar(50), @pass nvarchar(Max),``` @semat nvarchar(50), @tell nvarchar(50)

as insert into tbl_karbar values (@family,@username,(select HASHBYTES('SHA1' ,@pass)),@semat,@tell)
Itemized answered 16/9, 2015 at 10:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.