user34537
user34537

Reputation:

Hash Password in C#? Bcrypt/PBKDF2

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?

Upvotes: 44

Views: 67967

Answers (9)

pcdev
pcdev

Reputation: 3062

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=
 */

Upvotes: 9

paracycle
paracycle

Reputation: 7850

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

Upvotes: 39

Alex from Jitbit
Alex from Jitbit

Reputation: 60902

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

Upvotes: 21

CyberFox
CyberFox

Reputation: 810

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);
    }
}

Upvotes: 2

Phil
Phil

Reputation: 1089

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;
    }
} 

Upvotes: 1

JPK
JPK

Reputation: 1364

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!

Upvotes: 11

Luke T O&#39;Brien
Luke T O&#39;Brien

Reputation: 2845

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

Upvotes: 3

thashiznets
thashiznets

Reputation: 433

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 :)

Upvotes: 1

Jay S
Jay S

Reputation: 7994

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

Upvotes: 1

Related Questions