43

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?

  • That article is talking about generating a key from a password to use for encryption something. That is a very different topic than hashing passwords. – hatchet - done with SOverflow Jul 10 '12 at 12:15
  • @hatchet exactly and that was the best i could fine. –  Jul 10 '12 at 12:16
  • If you actually want to hash passwords, you should look at `System.Security.Cryptography.SHA256Managed` – Kendall Frey Jul 10 '12 at 12:18
  • 1
    see http://stackoverflow.com/questions/481160/is-bcrypt-a-good-encryption-algorithm-to-use-in-c-where-can-i-find-it for some resources. If you google `hash passwords salt .net` you'll get lots of appropriate hits. – hatchet - done with SOverflow Jul 10 '12 at 12:20
  • ...in particular [Chris Marisic's answer](http://stackoverflow.com/a/6228051/71059) – AakashM Jul 10 '12 at 12:28
  • 5
    @KendallFrey Why should he? Then he'd need to implement a slowing and salting scheme himself, instead of using the build in PBKDF2 implementation. – CodesInChaos Jul 10 '12 at 12:29
  • @hatchet In practice we use the same algorithms to derive a key from a password, and to store hashes passwords. Namely bcrypt, scrypt and PBKDF2. – CodesInChaos Jul 10 '12 at 12:30
  • 1
    @hatchet: Yes but none of them explain HOW to do it in C#. They just say use it.... which i am TRYING to do (properly) –  Jul 10 '12 at 12:45
  • @acidzombie: There's a simple example in my answer here: http://stackoverflow.com/a/4330586/55847 – LukeH Jul 10 '12 at 14:17
  • I've also written up a little bit about how to handle this here: http://davismj.me/blog/bcrypt/ – Matthew James Davis Jan 28 '16 at 20:39

9 Answers9

39

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

paracycle
  • 7,665
  • 1
  • 30
  • 34
  • It appears their calculateHash is simply `.GetBytes(64)`. So... Am i to choose my hash bitsize and use GetBytes? –  Jul 10 '12 at 12:54
  • Since i am not intimate with the details of the PBKDF2 algorithm, i wouldn't want to speculate. However, it seems like, as you said, you can just choose a secure enough length for your hash and get that many bytes from the method. – paracycle Jul 13 '12 at 06:38
  • Some people still seem to be recommending SHA1 and SHA256 instead of PBKDF2. Is PBKDF2 better? – James Sep 08 '14 at 15:57
  • @James: They are not quite the same thing really. PBKDF2 is a Password Based Key Derivation Function which takes a password and derives a key from that. This key is meant to be used in cryptographic operations. OTOH, SHA is for hashing input bytes. While you could use PBKDF2 for a similar purpose, I don't think it would be good to use the generated key as a hash. – paracycle Sep 18 '14 at 19:17
  • 2
    FWIW, SimpleCrypto.NET appears to have some security problems due to its implementation. Definitely check out GitHub Issues for these types of projects before integrating them into your own project. – NathanAldenSr Mar 02 '15 at 15:29
  • 1
    I have create a simple library for .NET that uses Microsoft implementation of PBKDF2 (Rfc2898DerivedBytes), but provides an extremely simple interface on top of that similar to BCrypt.NET, so you generally need to call Compute(), save the result to db and later verify by calling Verify. It's called SimpleHashing.Net: https://github.com/ilya-git/SimpleHashing.Net, there is also a package on Nuget with the same name. Once again, it's not a self implementation, but a thin wrapper on top of MS implementation for easy use. – Ilya Chernomordik Aug 27 '15 at 13:52
18

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

Alex from Jitbit
  • 53,710
  • 19
  • 160
  • 149
  • You can use a base-64 conversion to convert the byte array into a string such as Convert.ToBase64String(bytes) which produces a shorter string than a hex digit string. – Maximvs Mar 17 '22 at 16:34
11

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!

Community
  • 1
  • 1
JPK
  • 1,324
  • 2
  • 14
  • 25
7

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=
 */
pcdev
  • 2,852
  • 2
  • 23
  • 39
  • The page/code you reference has a warning: The following code shows how to use KeyDerivation.Pbkdf2 to generate a shared secret key. It should not be used to hash a password for storage in a datastore. – MikeT Jan 19 '23 at 07:33
  • Thanks for pointing that out. That's a fairly recent change that wasn't there when I wrote my answer. I'll update it. – pcdev Jan 20 '23 at 18:02
3

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

Community
  • 1
  • 1
Luke T O'Brien
  • 2,565
  • 3
  • 26
  • 38
  • 2
    To any other latecomers, the above-linked was helpful as vanilla code instead of deconstructing the implementation. (Not sure if it's useful or policy to edit / re-post SO code or the other code, so..) The 3 functions are: `HashPassword` and `VerifyHashedPassword` a the [link 1](http://stackoverflow.com/questions/20621950/asp-net-identity-default-password-hasher-how-does-it-work-and-is-it-secure), and then `ByteArraysEqual` at [link 2 alternate](http://forums.asp.net/t/2037899.aspx?password+handling+for+asp+net+with+c+). – zanlok Aug 19 '15 at 15:28
  • Very helpful. An alternative link would be the mono source code (which should be identical and a stable link): https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Helpers/Crypto.cs – Monkey Code Nov 09 '15 at 08:50
2

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);
    }
}
CyberFox
  • 780
  • 6
  • 24
  • 1
    This actually is the best answer for me, no extra libraries, it is in `System.Security.Cryptography` and even the hashing algorithm is selectable. Very nice, worked at the first try! (Btw most probably this class wasn't in the .net when the question was asked) – beatcoder Dec 03 '19 at 05:02
  • @beatcoder `Rfc2898DeriveBytes` has been in .NET since .NET Framework 2.0 which was released in October 2005 - https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes?redirectedfrom=MSDN&view=netframework-2.0 – Dave Black May 24 '23 at 16:12
1

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

Jay S
  • 7,904
  • 2
  • 39
  • 52
  • 1
    I have and it doesnt show how to hash a password. It shows how to encrypt. –  Jul 10 '12 at 12:49
  • 1
    My apologies, you're right this is about encryption for the most part, but I was hoping you might be able to leverage the class to get to what you needed. God job by paracycle to find the wrapper around it! – Jay S Jul 13 '12 at 20:05
1

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

thashiznets
  • 433
  • 4
  • 6
1

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;
    }
} 
Phil
  • 1,062
  • 1
  • 17
  • 15