11

I would like to find a solution or method that will allow me to add salt and control the number of iterations. The native Rfc2898DeriveBytes is based on HMACSHA1. Ideally, using SHA-256 or SHA-512 will make the system future proof.

This is the best example I have found so far: http://jmedved.com/2012/04/pbkdf2-with-sha-256-and-others/ but when I ran it with SHA-256 it was actually slower than with SHA-512. I used 64k iterations, a guid for salt and different same length passwords to compare.

I also found this solution: http://sourceforge.net/projects/pwdtknet/ which has full source code available. It seems to be more robust.

So far I am not able to get the same output from each of them.

jpshook
  • 4,834
  • 6
  • 36
  • 45
  • 2
    SHA is too fast for password hashing. – SLaks May 02 '13 at 15:16
  • possible duplicate of [Hashing passwords with MD5 or sha-256 C#](http://stackoverflow.com/questions/4329909/hashing-passwords-with-md5-or-sha-256-c-sharp) – Tim B James May 02 '13 at 15:18
  • @slaks - Depends how many times you do it I guess! – KingCronus May 02 '13 at 15:20
  • 1
    That question does not include PDKDF2 HMAC, Scrypt or SHA3. Not sure why you marked as duplicate. – jpshook May 02 '13 at 15:21
  • @slaks - What is your recommendation for password hashing? – jpshook May 02 '13 at 15:22
  • 1
    @Developr: PBKDFv2, bcrypt, or scrypt. http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html – SLaks May 02 '13 at 15:28
  • 4
    The .Net framework includes a PBKDF2 implementation in the [`Rfc2898DeriveBytes` class](http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx). Use that. – SLaks May 02 '13 at 15:17
  • Wow...down vote with no comment? I thought this was a place to find help. Btw, the existing .NET Rfc2898DeriveBytes is based on HMACSHA1. SHA1 is not a good solution. – jpshook May 02 '13 at 15:41
  • 1
    @SLaks This question only mentioned SHA in the context of PBKDF2, so it's fine. Calling 3DES insecure is an exaggeration as well. It's impractical to break properly used 3DES. @.Developr The collision weaknesses in SHA1 do not apply to password hashing. So there is no practical issue with PBKDF2-HMAC-SHA1. The biggest issue is that .net's implementation has bad performance forcing you to use a lower iteration count that you could otherwise. – CodesInChaos May 02 '13 at 15:46
  • 1
    I believe that BouncyCastle contains a PBKDF2 implementation that can be used with SHA-2. So you might want to look into that. Using a GUID as salt is fine, a salt only need to be unique, not unguessable. | On 64 bit CPUs I recommend using SHA-512 over SHA-256. – CodesInChaos May 02 '13 at 15:47
  • @CodesInChaos - Thanks for your comments. Would you mind taking a look at the code I posted a link to and give me your opinion? I will look at the BouncyCastle. – jpshook May 02 '13 at 16:02
  • @Developr At a glance I see no glaring issues. But I wouldn't use it without at least comparing against some standard testvectors. – CodesInChaos May 02 '13 at 16:07
  • @CodesInChaos Sounds good. I just found this (http://sourceforge.net/projects/pwdtknet/) as well that looks to be a perfect solution. – jpshook May 02 '13 at 16:14
  • 1
    @Slaks is right, anything quick is susceptible to brute force attacks however elegant and irreversible the algorithm. – Jodrell May 02 '13 at 16:38
  • 3
    I think people are missing the point regarding SHA being fast.SHA is fast, that's why you hash with a key stretching algorithm such as PBKDF2.As GPU speed increases you up the iterations and rehash the passwords as users login. OP isn't asking if this is a good solution or not (Which it is!!!) merely a .net implementation that uses something better than SHA1 for the pseudorandom function. – Oli May 02 '13 at 17:19
  • @Duncan Jones - All I really asked for was a solution on how to implement PBKDF2 HMAC SHA-512 for password hashing. Not sure how that is not constructive. How about providing a answer? – jpshook May 03 '13 at 12:28
  • @Developr Perhaps I misjudged your question then. When you began to talk about different speeds, you seem to have triggered a debate about whether you should be doing what you're doing. I guess that's not your fault, however. – Duncan Jones May 03 '13 at 12:36
  • @Duncan Jones - Thanks for your comment. I am not sure where/when the speed issue was brought up as it is not in my question or even relevant IHMO. – jpshook May 03 '13 at 15:39
  • PBKDF2 is intended for *client side* attack prevention, where the client has direct access to the hashed password. It forces a delay between brute-forcing by having a large CPU cost. Given that you are writing for ASP.NET, a much simpler HMAC would make your server CPUs far happier. Enforce a try limit or delay for the clients some other way. – Cory Nelson May 03 '13 at 21:54
  • Why not BCrypt or SCrypt? BCrypt.Net for example makes this trivial. Also, those interested in the security-focused parts of this discussion may not be aware of http://security.stackexchange.com/ which covers these issues and then some. For example: http://security.stackexchange.com/questions/6623/pre-hash-password-before-applying-bcrypt-to-avoid-restricting-password-length – Chris Moschini May 03 '13 at 22:00
  • @CoryNelson PBKDF2 is also important for passwords stored on the server. When the server gets compromised, and attacker can often learn both the key and HMAC. So it's still recommended to use a slow hash for passwords stored on the server. – CodesInChaos May 04 '13 at 18:37

6 Answers6

4

The PWDTK.NET library (http://sourceforge.net/projects/pwdtknet/) seems to be the only implementation I can find that does PBKDF2 HMAC SHA-512 and allows for salt and iterations. I have not been able to locate test vectors for PBKDF2 HMAC SHA-512 to test with.

I am surprised that there are not more devs out there using this already.

Not a big fan of answering my own questions, but since the comments degraded into a discussion about speed and no one else has answered yet, I might as well.

Thanks for all who commented.

jpshook
  • 4,834
  • 6
  • 36
  • 45
  • 3
    Hi Developer, I am HDizzle the author of PWDTK.NET. You are correct regarding the scarce availability of test vectors and so I have coded the framework such that the functions, attributes etc match those in the specification here: http://www.ietf.org/rfc/rfc2898.txt and so those with a bit of mathematical nouse are welcome to map the code to the maths. I realise test vectors would have been the ideal solution but I've had to make do with what I can. I'm yet to have someone find a fault in PWDTK tho and the source code has been downloaded by many so that is a good sign! Hope you like PWDTK! – thashiznets May 08 '13 at 23:43
  • 2
    You can install the PWDTK.NET package via NuGet ;-) – juFo Feb 17 '14 at 13:51
  • PBKDF2-HMAC-SHA-512 test vectors are available at the StackOverflow question [PBKDF2-HMAC-SHA-512 test vectors](http://stackoverflow.com/q/15593184/1967612) posted just a couple weeks after you'd asked this question! – Anti-weakpasswords Mar 03 '14 at 03:35
2

My CryptSharp library can do PBKDF2 with any arbitrary HMAC. Salt and iterations can be controlled. Look in the CryptSharp.Utility namespace. It's there along with a C# Scrypt implementation and a couple other things.

James
  • 1,874
  • 1
  • 16
  • 18
  • Dude, I had a look at this and utilising your demonstation: string cryptedPassword = Crypter.Blowfish.Crypt(password); or string cryptedPassword = Crypter.Blowfish.Crypt(password, Crypter.Blowfish.GenerateSalt(6)); You are not promoting the storing of salt in anyway.....need to keep the salt! So I recommend you add String salt = Crypter.Blowfish.GenerateSalt(64); <- Bigger salt for one And then go Crypter.Blowfish.Crypt(password, salt); make it a bit more user friendly for the punters ;) – thashiznets May 10 '13 at 03:16
  • What? The Crypt password format embeds algorithm parameters in the salt string. First, the rounds parameter on Blowfish crypt is log2. Second, it has nothing to do with the length of the salt. If you don't specify the salt, Crypt calls it automatically with a reasonable default parameter. Last, for Blowfish crypt, valid values are 4 to 31, corresponding to 2^4 to 2^31 rounds. – James May 10 '13 at 13:36
  • I should add that the Crypt format stores the salt in the final password string. You do not have to store the salt separately -- *that's* what makes it user friendly. :) Format is [algorithm][salt][password]. GenerateSalt gives you [algorithm][salt], Crypt fills in the last bit which you then store in a database. – James May 10 '13 at 13:43
  • So if GenerateSalt gives you [algorithm][salt] you then need to store [algorithm][salt] otherwise how do you perform a comparison? – thashiznets May 17 '13 at 10:24
  • No, you are already storing [algorithm][salt][password] (the "hashed password"), as that is the output of Crypt. That contains [algorithm][salt]. It's far easier than you are thinking. See http://en.wikipedia.org/wiki/Crypt_(C) for more details. – James May 17 '13 at 16:27
  • So, code-wise, you compare crypt(testPassword, hashedPassword) with hashedPassword when verifying. – James May 17 '13 at 16:28
  • Oh I see, so you need to strip the salt off the result so that you can supply it with the password each log on right? – thashiznets May 31 '13 at 05:22
2

This is provided by SecurityDriven.NET's Inferno library.

Install-Package Inferno

Inferno promotes SHA-384 given it's usage by the NSA Suite B for protecting top secret information and "its truncated design serves as an effective defense against length extension attacks" (1).

using SecurityDriven.Inferno;
using SecurityDriven.Inferno.Extensions;
using static SecurityDriven.Inferno.SuiteB;
using static SecurityDriven.Inferno.Utils;
using PBKDF2 = SecurityDriven.Inferno.Kdf.PBKDF2;

Storing an user's password:

var sha384Factory = HmacFactory;
var random = new CryptoRandom();

byte[] derivedKey
string hashedPassword = null;
string passwordText = "foo";

byte[] passwordBytes = SafeUTF8.GetBytes(passwordText);
var salt = random.NextBytes(384/8);

using (var pbkdf2 = new PBKDF2(sha384Factory, passwordBytes, salt, 256*1000))
    derivedKey=  pbkdf2.GetBytes(384/8);


using (var hmac = sha384Factory()) 
{
    hmac.Key = derivedKey;
    hashedPassword = hmac.ComputeHash(passwordBytes).ToBase16();
}

Persist both salt and hashedPassword. Note you can persist them either as binrary or use the helper to store them as strings. Note that salt is created randomly.

Verify a user's sign on:

var user = GetUserByUserName("bob")

var sha384Factory = HmacFactory;

byte[] derivedKey
string hashedPassword = null;
string suppliedPassword = "foo";

byte[] passwordBytes = SafeUTF8.GetBytes(suppliedPassword);

using (var pbkdf2 = new PBKDF2(sha384Factory, passwordBytes, user.UserSalt, 256*1000))
    derivedKey=  pbkdf2.GetBytes(384/8);


using (var hmac = sha384Factory()) 
{
    hmac.Key = derivedKey;
    hashedPassword = hmac.ComputeHash(passwordBytes).ToBase16();
}

isAuthenticated = hashedPassword == user.UserHashedPassword; //true for bob

As you can see here, the process is nearly identical. The key difference being that there's no usage of CryptoRandom and we use persisted UserSalt when creating the PBKDF2 instance.

Source on GitHub

Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
1

My open-source C# password utilities library on Google Code currently does HMAC SHA1-160 and HMAC SHA2-256, together with salt and iterations (PKDBF2). Timings for password and hash production is built-in to the library, as demonstrated by the accompanying Windows Forms gui.

My code currently takes 0.80 seconds on my machine to do SHA2-256 hash with 65,536 iterations. It could definitely be more efficient as I haven't profiled it yet.

My SHA2-256 code produces the same test results as shown here.

Community
  • 1
  • 1
HTTP 410
  • 17,300
  • 12
  • 76
  • 127
1

Another implementation - from before I found others like RoadWarrior, Zer and thasiznets had already done it.

This, like Rfc2898DeriveBytes derives from .NET's System.Cryptography.DeriveBytes. In other words, usage is the same - although I only implemented the one constructor I use.

Other than that lineage, it's not based on Microsoft's implementation at all. Which also calls for a disclaimer - see bottom of this answer.

It allows an arbitrary Pseudo Random Function, which means we can plug in HMAC SHA256 or HMAC SHA512 - or someone with more cryptographic insight and courage than me could plug in whatever they want - like the RFC allows. It also uses long rather than int for the iteration count - just for the crazy ones.

/// <summary>
/// More generic version of the built-in Rfc2898DeriveBytes class. This one
/// allows an arbitrary Pseudo Random Function, meaning we can use e.g. 
/// HMAC SHA256 or HMAC SHA512 rather than the hardcoded HMAC SHA-1 of the 
/// built-in version.
/// </summary>
public class PBKDF2DeriveBytes : DeriveBytes
{
    // Initialization:

    private readonly IPseudoRandomFunction prf;
    private readonly byte[] salt;
    private readonly long iterationCount;

    private readonly byte[] saltAndBlockNumber;

    // State:

    // Last result of prf.Transform - also used as buffer
    // between GetBytes() calls:
    private byte[] buffer;

    private int bufferIndex;
    private int nextBlock;

    /// <param name="prf">
    ///    The Pseudo Random Function to use for calculating the derived key
    /// </param>
    /// <param name="salt">
    ///    The initial salt to use in calculating the derived key
    /// </param>
    /// <param name="iterationCount">
    ///    Number of iterations. RFC 2898 recommends a minimum of 1000
    ///    iterations (in the year 2000) ideally with number of iterations
    ///    adjusted on a regular basis (e.g. each year).
    /// </param>
    public PBKDF2DeriveBytes(
       IPseudoRandomFunction prf, byte[] salt, long iterationCount)
    {
        if (prf == null)
        {
            throw new ArgumentNullException("prf");
        }

        if (salt == null)
        {
            throw new ArgumentNullException("salt");
        }

        this.prf = prf;
        this.salt = salt;
        this.iterationCount = iterationCount;

        // Prepare combined salt = concat(original salt, block number)
        saltAndBlockNumber = new byte[salt.Length + 4];
        Buffer.BlockCopy(salt, 0, saltAndBlockNumber, 0, salt.Length);

        Reset();
    }

    /// <summary>
    ///    Retrieves a derived key of the length specified.
    ///    Successive calls to GetBytes will return different results -
    ///    calling GetBytes(20) twice is equivalent to calling
    ///    GetBytes(40) once. Use Reset method to clear state.
    /// </summary>
    /// <param name="keyLength">
    ///    The number of bytes required. Note that for password hashing, a
    ///    key length greater than the output length of the underlying Pseudo
    ///    Random Function is redundant and does not increase security.
    /// </param>
    /// <returns>The derived key</returns>
    public override byte[] GetBytes(int keyLength)
    {
        var result = new byte[keyLength];

        int resultIndex = 0;

        // If we have bytes in buffer from previous run, use those first:
        if (buffer != null && bufferIndex > 0)
        {
            int bufferRemaining = prf.HashSize - bufferIndex;

            // Take at most keyLength bytes from the buffer:
            int bytesFromBuffer = Math.Min(bufferRemaining, keyLength);

            if (bytesFromBuffer > 0)
            {
                Buffer.BlockCopy(buffer, bufferIndex, result, 0,
                   bytesFromBuffer);
                bufferIndex += bytesFromBuffer;
                resultIndex += bytesFromBuffer;
            }
        }

        // If, after filling from buffer, we need more bytes to fill
        // the result, they need to be computed:
        if (resultIndex < keyLength)
        {
            ComputeBlocks(result, resultIndex);

            // If we used the entire buffer, reset index:
            if (bufferIndex == prf.HashSize)
            {
                bufferIndex = 0;
            }
        }

        return result;
    }

    /// <summary>
    ///    Resets state. The next call to GetBytes will return the same
    ///    result as an initial call to GetBytes.
    ///    Sealed since it's called from constructor.
    /// </summary>
    public sealed override void Reset()
    {
        buffer = null;
        bufferIndex = 0;
        nextBlock = 1;
    }

    private void ComputeBlocks(byte[] result, int resultIndex)
    {
        int currentBlock = nextBlock;

        // Keep computing blocks until we've filled the result array:
        while (resultIndex < result.Length)
        {
            // Run iterations for block:
            F(currentBlock);

            // Populate result array with the block, but only as many bytes
            // as are needed - keep the rest in buffer:
            int bytesFromBuffer = Math.Min(
                   prf.HashSize,
                   result.Length - resultIndex
            );
            Buffer.BlockCopy(buffer, 0, result, resultIndex, bytesFromBuffer);

            bufferIndex = bytesFromBuffer;
            resultIndex += bytesFromBuffer;
            currentBlock++;
        }
        nextBlock = currentBlock;
    }

    private void F(int currentBlock)
    {
        // First iteration:
        // Populate initial salt with the current block index:
        Buffer.BlockCopy(
           BlockNumberToBytes(currentBlock), 0, 
           saltAndBlockNumber, salt.Length, 4
        );

        buffer = prf.Transform(saltAndBlockNumber);

        // Remaining iterations:
        byte[] result = buffer;
        for (long iteration = 2; iteration <= iterationCount; iteration++)
        {
            // Note that the PRF transform takes the immediate result of the
            // last iteration, not the combined result (in buffer):
            result = prf.Transform(result);

            for (int byteIndex = 0; byteIndex < buffer.Length; byteIndex++)
            {
                buffer[byteIndex] ^= result[byteIndex];
            }
        }
    }

    private static byte[] BlockNumberToBytes(int blockNumber)
    {
        byte[] result = BitConverter.GetBytes(blockNumber);

        // Make sure the result is big endian:
        if (BitConverter.IsLittleEndian)
        {
            Array.Reverse(result);
        }

        return result;
    }
}

IPseudoRandomFunction is declared as:

public interface IPseudoRandomFunction : IDisposable
{
    int HashSize { get; }
    byte[] Transform(byte[] input);
}

An example HMAC-SHA512 IPseudoRandomFunction (for brevity - I use a generic class allowing any of .NET's HMAC classes):

public class HMACSHA512PseudoRandomFunction : IPseudoRandomFunction
{
    private HMAC hmac;
    private bool disposed;

    public HmacPseudoRandomFunction(byte[] input)
    {
        hmac = new HMACSHA512(input);
    }

    public int HashSize
    {
        // Might as well return a constant 64
        get { return hmac.HashSize / 8; }
    }

    public byte[] Transform(byte[] input)
    {
        return hmac.ComputeHash(input);
    }

    public void Dispose()
    {
        if (!disposed)
        {
            hmac.Dispose();
            hmac = null;
            disposed = true;
        }
    }
}

Result... This:

using (var prf = new HMACSHA512PseudoRandomFunction(input))
{
    using (var hash = new PBKDF2DeriveBytes(prf, salt, 1000))
    {
        hash.GetBytes(32);
    }
}

... is the HMAC-SHA512 equivalent of this:

using (var hash = new Rfc2898DeriveBytes(input, salt, 1000))
{
    hash.GetBytes(32);
}

Testing

The PBKDF2DeriveBytes class has been tested for

It has also been run through simple tests of Reset() and multiple calls to GetBytes().

A few preliminary performance tests reveal it to be on par with the .NET implementation for SHA-1 for 1000 runs of 1000 iterations on "pass"/"saltSALT" converted to bytes in ASCII encoding with GetBytes(200). Sometimes a little faster than the built-in implementation, sometimes a little slower - we're talking something like 84 vs. 83 seconds on my ancient computer. All of those were done with a debug build of PBKDF2DeriveBytes, though (since the bulk of the work is obviously done in the HMAC, we'd need many more iterations or runs to measure an actual difference anyway).

Disclaimer:

I'm not a cryptography genius. As the above indicates, this has not been heavily tested. I make no guarantees. But maybe, along with the other answers and implementations, it can help in understanding the methodology.

Community
  • 1
  • 1
JimmiTh
  • 7,389
  • 3
  • 34
  • 50
1

The more recent alternative is Microsoft.AspNetCore.Cryptography.KeyDerivation NuGet package, which allows to use PBKDF2 with SHA-256 and SHA-512 hash functions, which are stronger than SHA-1 which is built into Rfc2898DeriveBytes. The advantage over third-party libraries mentioned in other answers is that it's implemented by Microsoft, so you don't need to perform a security audit for it, once you already rely on .NET platform. Documentation is available at learn.microsoft.com.

ovolko
  • 2,777
  • 2
  • 21
  • 26