public byte[] DeriveKey(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested) { Debug.Assert(password != null); Debug.Assert(salt != null); Debug.Assert(iterationCount > 0); Debug.Assert(numBytesRequested > 0); // PBKDF2 is defined in NIST SP800-132, Sec. 5.3. // http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf byte[] retVal = new byte[numBytesRequested]; int numBytesWritten = 0; int numBytesRemaining = numBytesRequested; // For each block index, U_0 := Salt || block_index byte[] saltWithBlockIndex = new byte[checked(salt.Length + sizeof(uint))]; Buffer.BlockCopy(salt, 0, saltWithBlockIndex, 0, salt.Length); using (var hashAlgorithm = PrfToManagedHmacAlgorithm(prf, password)) { for (uint blockIndex = 1; numBytesRemaining > 0; blockIndex++) { // write the block index out as big-endian saltWithBlockIndex[saltWithBlockIndex.Length - 4] = (byte)(blockIndex >> 24); saltWithBlockIndex[saltWithBlockIndex.Length - 3] = (byte)(blockIndex >> 16); saltWithBlockIndex[saltWithBlockIndex.Length - 2] = (byte)(blockIndex >> 8); saltWithBlockIndex[saltWithBlockIndex.Length - 1] = (byte)blockIndex; // U_1 = PRF(U_0) = PRF(Salt || block_index) // T_blockIndex = U_1 byte[] U_iter = hashAlgorithm.ComputeHash(saltWithBlockIndex); // this is U_1 byte[] T_blockIndex = U_iter; for (int iter = 1; iter < iterationCount; iter++) { U_iter = hashAlgorithm.ComputeHash(U_iter); XorBuffers(src: U_iter, dest: T_blockIndex); // At this point, the 'U_iter' variable actually contains U_{iter+1} (due to indexing differences). } // At this point, we're done iterating on this block, so copy the transformed block into retVal. int numBytesToCopy = Math.Min(numBytesRemaining, T_blockIndex.Length); Buffer.BlockCopy(T_blockIndex, 0, retVal, numBytesWritten, numBytesToCopy); numBytesWritten += numBytesToCopy; numBytesRemaining -= numBytesToCopy; } } // retVal := T_1 || T_2 || ... || T_n, where T_n may be truncated to meet the desired output length return retVal; }
/// <summary> /// Performs key derivation using the PBKDF2 algorithm. /// </summary> /// <param name="password">The password from which to derive the key.</param> /// <param name="salt">The salt to be used during the key derivation process.</param> /// <param name="prf">The pseudo-random function to be used in the key derivation process.</param> /// <param name="iterationCount">The number of iterations of the pseudo-random function to apply /// during the key derivation process.</param> /// <param name="numBytesRequested">The desired length (in bytes) of the derived key.</param> /// <returns>The derived key.</returns> /// <remarks> /// The PBKDF2 algorithm is specified in RFC 2898. /// </remarks> public static byte[] Pbkdf2(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested) { if (password == null) { throw new ArgumentNullException(nameof(password)); } if (salt == null) { throw new ArgumentNullException(nameof(salt)); } // parameter checking if (prf < KeyDerivationPrf.HMACSHA1 || prf > KeyDerivationPrf.HMACSHA512) { throw new ArgumentOutOfRangeException(nameof(prf)); } if (iterationCount <= 0) { throw new ArgumentOutOfRangeException(nameof(iterationCount)); } if (numBytesRequested <= 0) { throw new ArgumentOutOfRangeException(nameof(numBytesRequested)); } return Pbkdf2Util.Pbkdf2Provider.DeriveKey(password, salt, prf, iterationCount, numBytesRequested); }
private static BCryptAlgorithmHandle PrfToCachedCngAlgorithmInstance(KeyDerivationPrf prf) { switch (prf) { case KeyDerivationPrf.HMACSHA1: return CachedAlgorithmHandles.HMAC_SHA1; case KeyDerivationPrf.HMACSHA256: return CachedAlgorithmHandles.HMAC_SHA256; case KeyDerivationPrf.HMACSHA512: return CachedAlgorithmHandles.HMAC_SHA512; default: throw CryptoUtil.Fail("Unrecognized PRF."); } }
private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested) { // Produce a version 3 (see comment above) text hash. byte[] salt = new byte[saltSize]; rng.GetBytes(salt); byte[] 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, (uint)iterCount); WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize); Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length); Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length); return outputBytes; }
private static byte[] HashPasswordV3(string password, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested) { // Produce a version 3 (see comment above) text hash. byte[] salt = new byte[saltSize]; using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(salt); } byte[] 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, (uint)iterCount); WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize); Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length); Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length); return(outputBytes); }
private byte[] ComputeHash(string password, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested) { byte[] salt = new byte[saltSize]; _rng.GetBytes(salt); byte[] subkey = _pbdkf2Provider.DeriveKey(password, salt, prf, iterCount, numBytesRequested); var outputBytes = new byte[13 + salt.Length + subkey.Length]; outputBytes[0] = 0x01; // format marker -- version WriteNetworkByteOrder(outputBytes, 1, (uint)prf); WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount); WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize); Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length); Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length); return(outputBytes); }
public static byte[] HashPasswordV2(string password) { RandomNumberGenerator rng = RandomNumberGenerator.Create(); 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); }
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 (see comment above) text hash. var salt = new byte[saltSize]; rng.GetBytes(salt); var 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); }
public static bool VerifyHashedPassword(byte[] hashedPassword, string guess) { KeyDerivationPrf prf = (KeyDerivationPrf)ReadNetworkByteOrder(hashedPassword, 1); int iterCount = (int)ReadNetworkByteOrder(hashedPassword, 5); int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9); byte[] salt = new byte[saltLength]; Buffer.BlockCopy(hashedPassword, 13, salt, 0, salt.Length); int subkeyLength = hashedPassword.Length - 13 - salt.Length; byte[] expectedSubkey = new byte[subkeyLength]; Buffer.BlockCopy(hashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length); // Hash incoming password and verify it byte[] actualSubkey = KeyDerivation.Pbkdf2(guess, salt, prf, iterCount, subkeyLength); bool verified = ByteArraysEqual(actualSubkey, expectedSubkey); return(verified); }
private static bool VerifyHashedPassword(byte[] hashedPassword, string password) { int iterCount = default(int); try { // Read header information KeyDerivationPrf prf = (KeyDerivationPrf)ReadNetworkByteOrder(hashedPassword, 1); iterCount = (int)ReadNetworkByteOrder(hashedPassword, 5); int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9); // Read the salt: must be >= 128 bits if (saltLength < 128 / 8) { return(false); } byte[] salt = new byte[saltLength]; Buffer.BlockCopy(hashedPassword, 13, salt, 0, salt.Length); // Read the subkey (the rest of the payload): must be >= 128 bits int subkeyLength = hashedPassword.Length - 13 - salt.Length; if (subkeyLength < 128 / 8) { return(false); } byte[] expectedSubkey = new byte[subkeyLength]; Buffer.BlockCopy(hashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length); // Hash the incoming password and verify it byte[] actualSubkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, subkeyLength); return(CryptographicOperations.FixedTimeEquals(actualSubkey, expectedSubkey)); } catch { // This should never occur except in the case of a malformed payload, where // we might go off the end of the array. Regardless, a malformed payload // implies verification failed. return(false); } }
private static HashSalt Hash(string word) { const KeyDerivationPrf Pbkdf2Prf = KeyDerivationPrf.HMACSHA256; const int pbkdfIterCount = 1000; const int pbkdf2SubkeyLength = 256 / 8; const int saltSize = 128 / 8; byte[] salt = new byte[saltSize]; RandomNumberGenerator rng = RandomNumberGenerator.Create(); rng.GetBytes(salt); byte[] subkey = KeyDerivation.Pbkdf2(word, salt, Pbkdf2Prf, pbkdfIterCount, pbkdf2SubkeyLength); var outputBytes = new byte[1 + saltSize + pbkdf2SubkeyLength]; outputBytes[0] = 0x00; Buffer.BlockCopy(salt, 0, outputBytes, 1, saltSize); Buffer.BlockCopy(subkey, 0, outputBytes, 1 + saltSize, pbkdf2SubkeyLength); return(new HashSalt(outputBytes, salt)); }
public byte[] HashUserPassword(string password) { _logger.LogInformation("Hashing user password"); const KeyDerivationPrf pbkdf2Prf = KeyDerivationPrf.HMACSHA512; // 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 (see comment above) text hash. var salt = new byte[saltSize]; _randomNumberGenerator.Value.GetBytes(salt); var 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); }
/// <summary> /// Hash a password using PBKDF2 /// </summary> /// <param name="password">Plain text password to hash</param> /// <param name="iteration">Amount of iteration</param> /// <param name="salt">Salt used in hashing</param> /// <param name="function">Key derivation function</param> /// <returns>Byte array of hash</returns> public byte[] HashPassword(string password, int iteration, byte[] salt, KeyDerivationPrf function) { // Use Pbkdf2 on password and salt to create key. var key = KeyDerivation.Pbkdf2( password: password, salt: salt, prf: function, iterationCount: iteration, numBytesRequested: Constants.HashLength); // Create hash with the size of salt and key. var hashed = new byte[Constants.HashLength + Constants.saltLength]; // Copy the salt to the hashed array. Array.Copy(salt, 0, hashed, 0, Constants.saltLength); // Copy the key into the hashed array. Array.Copy(key, 0, hashed, Constants.saltLength, Constants.HashLength); return(hashed); }
public virtual byte[] Encrypt(string password) { //128 bit salt. 128/8 bytes byte[] salt = new byte[SaltSize / 8]; Random rnd = new Random(Seed); rnd.NextBytes(salt); this.Salt = salt; // derive a 256-bit subkey (use HMACSHA256 with 10,000 iterations) KeyDerivationPrf prf = KeyDerivationPrf.HMACSHA256; int numBytesRequested = 256 / 8; int iterationCount = 1000; return(KeyDerivation.Pbkdf2( password: password, salt: salt, prf: prf, iterationCount: iterationCount, numBytesRequested: numBytesRequested )); }
private static BCryptKeyHandle PasswordToPbkdfKeyHandle(string password, BCryptAlgorithmHandle pbkdf2AlgHandle, KeyDerivationPrf prf) { byte dummy; // CLR doesn't like pinning zero-length buffers, so this provides a valid memory address when working with zero-length buffers // Convert password string to bytes. // Allocate on the stack whenever we can to save allocations. int cbPasswordBuffer = Encoding.UTF8.GetMaxByteCount(password.Length); fixed (byte* pbHeapAllocatedPasswordBuffer = (cbPasswordBuffer > Constants.MAX_STACKALLOC_BYTES) ? new byte[cbPasswordBuffer] : null) { byte* pbPasswordBuffer = pbHeapAllocatedPasswordBuffer; if (pbPasswordBuffer == null) { if (cbPasswordBuffer == 0) { pbPasswordBuffer = &dummy; } else { byte* pbStackAllocPasswordBuffer = stackalloc byte[cbPasswordBuffer]; // will be released when the frame unwinds pbPasswordBuffer = pbStackAllocPasswordBuffer; } } try { int cbPasswordBufferUsed; // we're not filling the entire buffer, just a partial buffer fixed (char* pszPassword = password) { cbPasswordBufferUsed = Encoding.UTF8.GetBytes(pszPassword, password.Length, pbPasswordBuffer, cbPasswordBuffer); } return PasswordToPbkdfKeyHandleStep2(pbkdf2AlgHandle, pbPasswordBuffer, (uint)cbPasswordBufferUsed, prf); } finally { UnsafeBufferUtil.SecureZeroMemory(pbPasswordBuffer, cbPasswordBuffer); } } }
static void Main(string[] args) { 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]; using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(salt); } byte[] subkey = KeyDerivation.Pbkdf2(args[0], 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); Console.WriteLine($"{Convert.ToBase64String(outputBytes)}"); }
private static bool VerifyHashInternal(byte[] hashedData, string providedInput) { try { // Read header information KeyDerivationPrf prf = (KeyDerivationPrf)ReadNetworkByteOrder(hashedData, 1); int iterCount = (int)ReadNetworkByteOrder(hashedData, 5); int saltLength = (int)ReadNetworkByteOrder(hashedData, 9); // Read the salt: must be >= 128 bits if (saltLength < 128 / 8) { return(false); } byte[] salt = new byte[saltLength]; Buffer.BlockCopy(hashedData, 13, salt, 0, salt.Length); // Read the subkey (the rest of the payload): must be >= 128 bits int subkeyLength = hashedData.Length - 13 - salt.Length; if (subkeyLength < 128 / 8) { return(false); } byte[] expectedSubkey = new byte[subkeyLength]; Buffer.BlockCopy(hashedData, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length); // Hash the incoming credential and verify it byte[] actualSubkey = KeyDerivation.Pbkdf2(providedInput, salt, prf, iterCount, subkeyLength); return(ByteArraysEqual(actualSubkey, expectedSubkey)); } catch { // This should never occur except in the case of a malformed payload, where // we might go off the end of the array. Regardless, a malformed payload // implies verification failed. return(false); } }
private static bool VerifyHashedPasswordV2(byte[] hashedPassword, string password) { 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 // We know ahead of time the exact length of a valid hashed password payload. if (hashedPassword.Length != 1 + SaltSize + Pbkdf2SubkeyLength) { return(false); // bad size } byte[] salt = new byte[SaltSize]; Buffer.BlockCopy(hashedPassword, 1, salt, 0, salt.Length); byte[] expectedSubkey = new byte[Pbkdf2SubkeyLength]; Buffer.BlockCopy(hashedPassword, 1 + salt.Length, expectedSubkey, 0, expectedSubkey.Length); // Hash the incoming password and verify it byte[] actualSubkey = KeyDerivation.Pbkdf2(password, salt, Pbkdf2Prf, Pbkdf2IterCount, Pbkdf2SubkeyLength); return(ByteArraysEqual(actualSubkey, expectedSubkey)); }
private static KeyedHashAlgorithm PrfToManagedHmacAlgorithm(KeyDerivationPrf prf, string password) { byte[] passwordBytes = Encoding.UTF8.GetBytes(password); try { switch (prf) { case KeyDerivationPrf.HMACSHA1: return new HMACSHA1(passwordBytes); case KeyDerivationPrf.HMACSHA256: return new HMACSHA256(passwordBytes); case KeyDerivationPrf.HMACSHA512: return new HMACSHA512(passwordBytes); default: throw CryptoUtil.Fail("Unrecognized PRF."); } } finally { // The HMAC ctor makes a duplicate of this key; we clear original buffer to limit exposure to the GC. Array.Clear(passwordBytes, 0, passwordBytes.Length); } }
private static bool VerifyHashedPassword(byte[] hashedPassword, string password, out int iterCount) { iterCount = default(int); try { KeyDerivationPrf prf = (KeyDerivationPrf)ReadNetworkByteOrder(hashedPassword, 1); iterCount = (int)ReadNetworkByteOrder(hashedPassword, 5); int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9); if (saltLength < 128 / 8) { return(false); } byte[] salt = new byte[saltLength]; Buffer.BlockCopy(hashedPassword, 13, salt, 0, saltLength); int subKeyLength = hashedPassword.Length - 13 - salt.Length; if (subKeyLength < 128 / 8) { return(false); } byte[] expectedSubKey = new byte[subKeyLength]; Buffer.BlockCopy(hashedPassword, 13 + salt.Length, expectedSubKey, 0, expectedSubKey.Length); byte[] actualSubKey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, expectedSubKey.Length); return(CryptographicOperations.FixedTimeEquals(actualSubKey, expectedSubKey)); } catch { return(false); } }
private static bool VerifyHashedPasswordV3(byte[] hashedPassword, string password, out int iterCount) { iterCount = default(int); try { // Read header information KeyDerivationPrf prf = (KeyDerivationPrf)ReadNetworkByteOrder(hashedPassword, 1); iterCount = (int)ReadNetworkByteOrder(hashedPassword, 5); int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9); // Read the salt: must be >= 128 bits if (saltLength < 128 / 8) { return(false); } byte[] salt = new byte[saltLength]; Buffer.BlockCopy(hashedPassword, 13, salt, 0, salt.Length); // Read the subkey (the rest of the payload): must be >= 128 bits int subkeyLength = hashedPassword.Length - 13 - salt.Length; if (subkeyLength < 128 / 8) { return(false); } byte[] expectedSubkey = new byte[subkeyLength]; Buffer.BlockCopy(hashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length); // Hash the incoming password and verify it byte[] actualSubkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, subkeyLength); return(ByteArraysEqual(actualSubkey, expectedSubkey)); } catch { return(false); } }
private static byte[] HashPasswordV3 (string password, RandomNumberGenerator random, KeyDerivationPrf prf = KeyDerivationPrf.HMACSHA256, int iterCount = IterCount, int saltSize = 128 / 8, int numBytesRequested = 256 / 8) { // Produce a version 3 (see comment above) text hash. var salt = new byte[saltSize]; random.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, (uint)iterCount); WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize); Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length); Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length); return(outputBytes); }
private static byte[] DeriveKeyImpl(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested) { HashAlgorithmName algorithmName; switch (prf) { case KeyDerivationPrf.HMACSHA1: algorithmName = HashAlgorithmName.SHA1; break; case KeyDerivationPrf.HMACSHA256: algorithmName = HashAlgorithmName.SHA256; break; case KeyDerivationPrf.HMACSHA512: algorithmName = HashAlgorithmName.SHA512; break; default: throw new ArgumentOutOfRangeException(); } return(Rfc2898DeriveBytes.Pbkdf2(Encoding.UTF8.GetBytes(password), salt, iterationCount, algorithmName, numBytesRequested)); }
public byte[] DeriveKey(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested) { Debug.Assert(password != null); Debug.Assert(salt != null); Debug.Assert(iterationCount > 0); Debug.Assert(numBytesRequested > 0); string algorithmName = PrfToCngAlgorithmId(prf); fixed (byte* pbHeapAllocatedSalt = salt) { byte dummy; // CLR doesn't like pinning zero-length buffers, so this provides a valid memory address when working with zero-length buffers byte* pbSalt = (pbHeapAllocatedSalt != null) ? pbHeapAllocatedSalt : &dummy; byte[] retVal = new byte[numBytesRequested]; using (BCryptKeyHandle keyHandle = PasswordToPbkdfKeyHandle(password, CachedAlgorithmHandles.PBKDF2, prf)) { fixed (byte* pbRetVal = retVal) { DeriveKeyCore(keyHandle, algorithmName, pbSalt, (uint)salt.Length, (ulong)iterationCount, pbRetVal, (uint)retVal.Length); } return retVal; } } }
/// <summary> /// Verify given hashed value with plan string value after hashing with same settings, /// that was provided on hash /// </summary> /// <param name="hashedValue">string - Encrypted value</param> /// <param name="sourceSalt">string - Based 64 string</param> /// <param name="sourceToCheck">string - plan text</param> /// <param name="iterationCount">integer - Number of iteratation, default is 1000 </param> /// <param name="algorithm">Enum type of KeyDerivationPrf, default is HMACSHA256</param> /// <returns>boolean - return true if comparision success otherwise returns false</returns> public static bool VerifyHashString(string hashedValue, string sourceSalt, string sourceToCheck, int iterationCount = 1000, KeyDerivationPrf algorithm = KeyDerivationPrf.HMACSHA256) { if (hashedValue == null || sourceSalt == null) { return(false); } var salt = Convert.FromBase64String(sourceSalt); if (salt == null) { return(false); } // hash the given source var(hashOfstringToCheck, returnsalt) = GenerateHashString(sourceToCheck, iterationCount, algorithm, salt, true); // compare both hashes return(String.Compare(hashedValue, hashOfstringToCheck) == 0); }
private static BCryptKeyHandle PasswordToPbkdfKeyHandleStep2(BCryptAlgorithmHandle pbkdf2AlgHandle, byte* pbPassword, uint cbPassword, KeyDerivationPrf prf) { const uint PBKDF2_MAX_KEYLENGTH_IN_BYTES = 2048; // GetSupportedKeyLengths() on a Win8 box; value should never be lowered in any future version of Windows if (cbPassword <= PBKDF2_MAX_KEYLENGTH_IN_BYTES) { // Common case: the password is small enough to be consumed directly by the PBKDF2 algorithm. return pbkdf2AlgHandle.GenerateSymmetricKey(pbPassword, cbPassword); } else { // Rare case: password is very long; we must hash manually. // PBKDF2 uses the PRFs in HMAC mode, and when the HMAC input key exceeds the hash function's // block length the key is hashed and run back through the key initialization function. BCryptAlgorithmHandle prfAlgorithmHandle; // cached; don't dispose switch (prf) { case KeyDerivationPrf.HMACSHA1: prfAlgorithmHandle = CachedAlgorithmHandles.SHA1; break; case KeyDerivationPrf.HMACSHA256: prfAlgorithmHandle = CachedAlgorithmHandles.SHA256; break; case KeyDerivationPrf.HMACSHA512: prfAlgorithmHandle = CachedAlgorithmHandles.SHA512; break; default: throw CryptoUtil.Fail("Unrecognized PRF."); } // Final sanity check: don't hash the password if the HMAC key initialization function wouldn't have done it for us. if (cbPassword <= prfAlgorithmHandle.GetHashBlockLength() /* in bytes */) { return pbkdf2AlgHandle.GenerateSymmetricKey(pbPassword, cbPassword); } // Hash the password and use the hash as input to PBKDF2. uint cbPasswordDigest = prfAlgorithmHandle.GetHashDigestLength(); CryptoUtil.Assert(cbPasswordDigest > 0, "cbPasswordDigest > 0"); fixed (byte* pbPasswordDigest = new byte[cbPasswordDigest]) { try { using (var hashHandle = prfAlgorithmHandle.CreateHash()) { hashHandle.HashData(pbPassword, cbPassword, pbPasswordDigest, cbPasswordDigest); } return pbkdf2AlgHandle.GenerateSymmetricKey(pbPasswordDigest, cbPasswordDigest); } finally { UnsafeBufferUtil.SecureZeroMemory(pbPasswordDigest, cbPasswordDigest); } } } }
private static string PrfToCngAlgorithmId(KeyDerivationPrf prf) { switch (prf) { case KeyDerivationPrf.HMACSHA1: return Constants.BCRYPT_SHA1_ALGORITHM; case KeyDerivationPrf.HMACSHA256: return Constants.BCRYPT_SHA256_ALGORITHM; case KeyDerivationPrf.HMACSHA512: return Constants.BCRYPT_SHA512_ALGORITHM; default: throw CryptoUtil.Fail("Unrecognized PRF."); } }
private static BCryptKeyHandle PasswordToPbkdfKeyHandle(string password, BCryptAlgorithmHandle pbkdf2AlgHandle, KeyDerivationPrf prf) { byte dummy; // CLR doesn't like pinning zero-length buffers, so this provides a valid memory address when working with zero-length buffers // Convert password string to bytes. // Allocate on the stack whenever we can to save allocations. int cbPasswordBuffer = Encoding.UTF8.GetMaxByteCount(password.Length); fixed(byte *pbHeapAllocatedPasswordBuffer = (cbPasswordBuffer > Constants.MAX_STACKALLOC_BYTES)?new byte[cbPasswordBuffer] : null) { byte *pbPasswordBuffer = pbHeapAllocatedPasswordBuffer; if (pbPasswordBuffer == null) { if (cbPasswordBuffer == 0) { pbPasswordBuffer = &dummy; } else { byte *pbStackAllocPasswordBuffer = stackalloc byte[cbPasswordBuffer]; // will be released when the frame unwinds pbPasswordBuffer = pbStackAllocPasswordBuffer; } } try { int cbPasswordBufferUsed; // we're not filling the entire buffer, just a partial buffer fixed(char *pszPassword = password) { cbPasswordBufferUsed = Encoding.UTF8.GetBytes(pszPassword, password.Length, pbPasswordBuffer, cbPasswordBuffer); } return(PasswordToPbkdfKeyHandleStep2(pbkdf2AlgHandle, pbPasswordBuffer, (uint)cbPasswordBufferUsed, prf)); } finally { UnsafeBufferUtil.SecureZeroMemory(pbPasswordBuffer, cbPasswordBuffer); } } }
private static BCryptKeyHandle PasswordToPbkdfKeyHandleStep2(BCryptAlgorithmHandle pbkdf2AlgHandle, byte *pbPassword, uint cbPassword, KeyDerivationPrf prf) { const uint PBKDF2_MAX_KEYLENGTH_IN_BYTES = 2048; // GetSupportedKeyLengths() on a Win8 box; value should never be lowered in any future version of Windows if (cbPassword <= PBKDF2_MAX_KEYLENGTH_IN_BYTES) { // Common case: the password is small enough to be consumed directly by the PBKDF2 algorithm. return(pbkdf2AlgHandle.GenerateSymmetricKey(pbPassword, cbPassword)); } else { // Rare case: password is very long; we must hash manually. // PBKDF2 uses the PRFs in HMAC mode, and when the HMAC input key exceeds the hash function's // block length the key is hashed and run back through the key initialization function. BCryptAlgorithmHandle prfAlgorithmHandle; // cached; don't dispose switch (prf) { case KeyDerivationPrf.Sha1: prfAlgorithmHandle = CachedAlgorithmHandles.SHA1; break; case KeyDerivationPrf.Sha256: prfAlgorithmHandle = CachedAlgorithmHandles.SHA256; break; case KeyDerivationPrf.Sha512: prfAlgorithmHandle = CachedAlgorithmHandles.SHA512; break; default: throw CryptoUtil.Fail("Unrecognized PRF."); } // Final sanity check: don't hash the password if the HMAC key initialization function wouldn't have done it for us. if (cbPassword <= prfAlgorithmHandle.GetHashBlockLength() /* in bytes */) { return(pbkdf2AlgHandle.GenerateSymmetricKey(pbPassword, cbPassword)); } // Hash the password and use the hash as input to PBKDF2. uint cbPasswordDigest = prfAlgorithmHandle.GetHashDigestLength(); CryptoUtil.Assert(cbPasswordDigest > 0, "cbPasswordDigest > 0"); fixed(byte *pbPasswordDigest = new byte[cbPasswordDigest]) { try { using (var hashHandle = prfAlgorithmHandle.CreateHash()) { hashHandle.HashData(pbPassword, cbPassword, pbPasswordDigest, cbPasswordDigest); } return(pbkdf2AlgHandle.GenerateSymmetricKey(pbPasswordDigest, cbPasswordDigest)); } finally { UnsafeBufferUtil.SecureZeroMemory(pbPasswordDigest, cbPasswordDigest); } } } }
public byte[] DeriveKey(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested) { Debug.Assert(password != null); Debug.Assert(salt != null); Debug.Assert(iterationCount > 0); Debug.Assert(numBytesRequested > 0); byte dummy; // CLR doesn't like pinning zero-length buffers, so this provides a valid memory address when working with zero-length buffers // Don't dispose of this algorithm instance; it is cached and reused! var algHandle = PrfToCachedCngAlgorithmInstance(prf); // Convert password string to bytes. // Allocate on the stack whenever we can to save allocations. int cbPasswordBuffer = Encoding.UTF8.GetMaxByteCount(password.Length); fixed (byte* pbHeapAllocatedPasswordBuffer = (cbPasswordBuffer > Constants.MAX_STACKALLOC_BYTES) ? new byte[cbPasswordBuffer] : null) { byte* pbPasswordBuffer = pbHeapAllocatedPasswordBuffer; if (pbPasswordBuffer == null) { if (cbPasswordBuffer == 0) { pbPasswordBuffer = &dummy; } else { byte* pbStackAllocPasswordBuffer = stackalloc byte[cbPasswordBuffer]; // will be released when the frame unwinds pbPasswordBuffer = pbStackAllocPasswordBuffer; } } try { int cbPasswordBufferUsed; // we're not filling the entire buffer, just a partial buffer fixed (char* pszPassword = password) { cbPasswordBufferUsed = Encoding.UTF8.GetBytes(pszPassword, password.Length, pbPasswordBuffer, cbPasswordBuffer); } fixed (byte* pbHeapAllocatedSalt = salt) { byte* pbSalt = (pbHeapAllocatedSalt != null) ? pbHeapAllocatedSalt : &dummy; byte[] retVal = new byte[numBytesRequested]; fixed (byte* pbRetVal = retVal) { int ntstatus = UnsafeNativeMethods.BCryptDeriveKeyPBKDF2( hPrf: algHandle, pbPassword: pbPasswordBuffer, cbPassword: (uint)cbPasswordBufferUsed, pbSalt: pbSalt, cbSalt: (uint)salt.Length, cIterations: (ulong)iterationCount, pbDerivedKey: pbRetVal, cbDerivedKey: (uint)retVal.Length, dwFlags: 0); UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); } return retVal; } } finally { UnsafeBufferUtil.SecureZeroMemory(pbPasswordBuffer, cbPasswordBuffer); } } }
/// <summary> /// Simplified version of https://github.com/dotnet/aspnetcore/blob/9428ac6f4c2103227c46a4da23b4de9cb780dc08/src/Identity/Extensions.Core/src/PasswordHasher.cs#L141 /// Uses a fixed salt, as we need reproducible results /// </summary> private string Hash(string original, byte[] salt, KeyDerivationPrf prf, int iterCount, int numBytesRequested) { var hashBytes = KeyDerivation.Pbkdf2(original, salt, prf, iterCount, numBytesRequested); return(Convert.ToBase64String(hashBytes)); }
public static IServiceCollection RegisterCryptographicCredentials <TCryptographicCredentials>(this IServiceCollection services, KeyDerivationPrf keyDerivationPrf, Encoding encoding, string password, string salt, int iterations, int totalNumberOfBytes, IEnumerable <byte> initialVector) where TCryptographicCredentials : ICryptographicCredentials { return(services.AddSingleton <ICryptographicCredentials>(serviceProvider => serviceProvider .GetRequiredService <ICryptographyProvider>() .GetCryptographicCredentials <TCryptographicCredentials>(keyDerivationPrf, encoding, password, salt, iterations, totalNumberOfBytes, initialVector))); }
static PasswordHelper() { HashType = KeyDerivationPrf.HMACSHA512; }
/// <summary> /// Generate hash value using Password-Based Key Derivation Function 2 /// </summary> /// <param name="targetValue">string - Value to be hash</param> /// <param name="iterationCount">integer - Number of iteratation, default is 1000 </param> /// <param name="algorithm">KeyDerivationPrf - Enum type of KeyDerivationPrf, default is HMACSHA256 </param> /// <param name="salt">byte[] - 128/8 byte code, default is null</param> /// <param name="needsOnlyHash">boolean - condition to return hash</param> /// <returns>type Tuple - (hashed value, salt value)</returns> public static (string, string) GenerateHashString(string targetValue, int iterationCount = 1000, KeyDerivationPrf algorithm = KeyDerivationPrf.HMACSHA256, byte[] salt = null, bool needsOnlyHash = false) { if (salt == null || salt.Length != 16) { // generate a 128-bit salt using a secure PRNG salt = new byte[128 / 8]; using var rng = RandomNumberGenerator.Create(); rng.GetBytes(salt); } string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2( password: targetValue, salt: salt, prf: algorithm, iterationCount: iterationCount, numBytesRequested: 256 / 8)); if (needsOnlyHash) { return(hashed, string.Empty); } return(hashed, Convert.ToBase64String(salt)); }
public string DeriveKey(string passphrase, byte[] salt, int iterationCount = 10000, KeyDerivationPrf prf = KeyDerivationPrf.HMACSHA256, int numBytes = 32) { var key = KeyDerivation.Pbkdf2( password: passphrase, salt: salt, prf: prf, iterationCount: iterationCount, numBytesRequested: numBytes ); return(key.ToHex()); }
private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested) { byte[] salt = new byte[saltSize]; rng.GetBytes(salt); byte[] 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, (uint)iterCount); WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize); Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length); Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length); return(outputBytes); }
public SecretKeyGenerator(KeyDerivationPrf algorithm) => Algorithm = AssertUtils.AssertNotNull(algorithm);
public static string PrfName(KeyDerivationPrf prf) { return(Enum.GetName(typeof(KeyDerivationPrf), prf)); }
public static string GeneratePasswordHash(this string password, KeyDerivationPrf prf = KeyDerivationPrf.HMACSHA256, int iterationCount = 10000, int saltSize = 16) { return(PasswordManager.GeneratePasswordHash(password, prf, iterationCount, saltSize)); }
public byte[] DeriveKey(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested) { Debug.Assert(password != null); Debug.Assert(salt != null); Debug.Assert(iterationCount > 0); Debug.Assert(numBytesRequested > 0); byte dummy; // CLR doesn't like pinning zero-length buffers, so this provides a valid memory address when working with zero-length buffers // Don't dispose of this algorithm instance; it is cached and reused! var algHandle = PrfToCachedCngAlgorithmInstance(prf); // Convert password string to bytes. // Allocate on the stack whenever we can to save allocations. int cbPasswordBuffer = Encoding.UTF8.GetMaxByteCount(password.Length); fixed(byte *pbHeapAllocatedPasswordBuffer = (cbPasswordBuffer > Constants.MAX_STACKALLOC_BYTES)?new byte[cbPasswordBuffer] : null) { byte *pbPasswordBuffer = pbHeapAllocatedPasswordBuffer; if (pbPasswordBuffer == null) { if (cbPasswordBuffer == 0) { pbPasswordBuffer = &dummy; } else { byte *pbStackAllocPasswordBuffer = stackalloc byte[cbPasswordBuffer]; // will be released when the frame unwinds pbPasswordBuffer = pbStackAllocPasswordBuffer; } } try { int cbPasswordBufferUsed; // we're not filling the entire buffer, just a partial buffer fixed(char *pszPassword = password) { cbPasswordBufferUsed = Encoding.UTF8.GetBytes(pszPassword, password.Length, pbPasswordBuffer, cbPasswordBuffer); } fixed(byte *pbHeapAllocatedSalt = salt) { byte *pbSalt = (pbHeapAllocatedSalt != null) ? pbHeapAllocatedSalt : &dummy; byte[] retVal = new byte[numBytesRequested]; fixed(byte *pbRetVal = retVal) { int ntstatus = UnsafeNativeMethods.BCryptDeriveKeyPBKDF2( hPrf: algHandle, pbPassword: pbPasswordBuffer, cbPassword: (uint)cbPasswordBufferUsed, pbSalt: pbSalt, cbSalt: (uint)salt.Length, cIterations: (ulong)iterationCount, pbDerivedKey: pbRetVal, cbDerivedKey: (uint)retVal.Length, dwFlags: 0); UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); } return(retVal); } } finally { UnsafeBufferUtil.SecureZeroMemory(pbPasswordBuffer, cbPasswordBuffer); } } }