Created
August 20, 2013 10:53
-
-
Save nbevans/6279980 to your computer and use it in GitHub Desktop.
PBKDF2 functions for secure password storage, and supports custom iterations (defaults to a OWASP 2013 recommendation of 100,000). Created from decompiling the System.Web.Helpers.Crypto class.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Security.Cryptography; | |
namespace Nbevans.Cryptography { | |
public static class Pbkdf2 { | |
private const int Pbkdf2SubkeyLength = 32; | |
private const int SaltSize = 16; | |
private static bool ByteArraysEqual(byte[] a, byte[] b) { | |
if (ReferenceEquals(a, b)) return true; | |
if (a == null || b == null || a.Length != b.Length) return false; | |
var flag = true; | |
for (var i = 0; i < a.Length; i++) | |
flag = flag & a[i] == b[i]; | |
return flag; | |
} | |
public static string Hash(string password, int iterations = 100000) { | |
if (password == null) throw new ArgumentNullException("password"); | |
byte[] salt; | |
byte[] bytes; | |
using (var algo = new Rfc2898DeriveBytes(password, SaltSize, iterations)) { | |
salt = algo.Salt; | |
bytes = algo.GetBytes(Pbkdf2SubkeyLength); | |
} | |
var iters = BitConverter.GetBytes(iterations); | |
if (!BitConverter.IsLittleEndian) | |
Array.Reverse(iters); | |
var parts = new byte[54]; | |
Buffer.BlockCopy(salt, 0, parts, 1, SaltSize); | |
Buffer.BlockCopy(bytes, 0, parts, 17, Pbkdf2SubkeyLength); | |
Buffer.BlockCopy(iters, 0, parts, 50, sizeof(int)); | |
return Convert.ToBase64String(parts); | |
} | |
public static bool Verify(string hashedPassword, string password) { | |
if (hashedPassword == null) throw new ArgumentNullException("hashedPassword"); | |
if (password == null) throw new ArgumentNullException("password"); | |
var parts = Convert.FromBase64String(hashedPassword); | |
if (parts.Length != 54 || parts[0] != 0) | |
return false; | |
var salt = new byte[SaltSize]; | |
Buffer.BlockCopy(parts, 1, salt, 0, SaltSize); | |
var bytes = new byte[Pbkdf2SubkeyLength]; | |
Buffer.BlockCopy(parts, 17, bytes, 0, Pbkdf2SubkeyLength); | |
var iters = new byte[sizeof(int)]; | |
Buffer.BlockCopy(parts, 50, iters, 0, sizeof(int)); | |
if (!BitConverter.IsLittleEndian) | |
Array.Reverse(iters); | |
var iterations = BitConverter.ToInt32(iters, 0); | |
byte[] challengeBytes; | |
using (var algo = new Rfc2898DeriveBytes(password, salt, iterations)) | |
challengeBytes = algo.GetBytes(32); | |
return ByteArraysEqual(bytes, challengeBytes); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment