diff --git a/src/Microsoft.AspNet.Security.DataProtection/Algorithms.cs b/src/Microsoft.AspNet.Security.DataProtection/Algorithms.cs
index a2e3589613..bd721c207c 100644
--- a/src/Microsoft.AspNet.Security.DataProtection/Algorithms.cs
+++ b/src/Microsoft.AspNet.Security.DataProtection/Algorithms.cs
@@ -31,11 +31,11 @@ namespace Microsoft.AspNet.Security.DataProtection
return algHandle;
}
- private static BCryptAlgorithmHandle CreateHMACSHA256AlgorithmHandle()
+
+ internal static BCryptAlgorithmHandle CreateGenericHMACHandleFromPrimitiveProvider(string algorithmName)
{
- // create the HMACSHA-256 instance
BCryptAlgorithmHandle algHandle;
- int status = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algHandle, Constants.BCRYPT_SHA256_ALGORITHM, Constants.MS_PRIMITIVE_PROVIDER, dwFlags: BCryptAlgorithmFlags.BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ int status = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algHandle, algorithmName, Constants.MS_PRIMITIVE_PROVIDER, dwFlags: BCryptAlgorithmFlags.BCRYPT_ALG_HANDLE_HMAC_FLAG);
if (status != 0 || algHandle == null || algHandle.IsInvalid)
{
throw new CryptographicException(status);
@@ -44,17 +44,16 @@ namespace Microsoft.AspNet.Security.DataProtection
return algHandle;
}
+ private static BCryptAlgorithmHandle CreateHMACSHA256AlgorithmHandle()
+ {
+ // create the HMACSHA-256 instance
+ return CreateGenericHMACHandleFromPrimitiveProvider(Constants.BCRYPT_SHA256_ALGORITHM);
+ }
+
private static BCryptAlgorithmHandle CreateHMACSHA512AlgorithmHandle()
{
// create the HMACSHA-512 instance
- BCryptAlgorithmHandle algHandle;
- int status = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algHandle, Constants.BCRYPT_SHA512_ALGORITHM, Constants.MS_PRIMITIVE_PROVIDER, dwFlags: BCryptAlgorithmFlags.BCRYPT_ALG_HANDLE_HMAC_FLAG);
- if (status != 0 || algHandle == null || algHandle.IsInvalid)
- {
- throw new CryptographicException(status);
- }
-
- return algHandle;
+ return CreateGenericHMACHandleFromPrimitiveProvider(Constants.BCRYPT_SHA512_ALGORITHM);
}
}
}
diff --git a/src/Microsoft.AspNet.Security.DataProtection/PBKDF2.cs b/src/Microsoft.AspNet.Security.DataProtection/PBKDF2.cs
new file mode 100644
index 0000000000..c8824b37c1
--- /dev/null
+++ b/src/Microsoft.AspNet.Security.DataProtection/PBKDF2.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Security.Cryptography;
+
+namespace Microsoft.AspNet.Security.DataProtection
+{
+ ///
+ /// Helper class to derive keys from low-entropy passwords using the PBKDF2 algorithm.
+ ///
+ public static class PBKDF2
+ {
+ ///
+ /// Derives a key from a low-entropy password.
+ ///
+ /// The name of the PRF to use for key derivation.
+ /// The low-entropy password from which to generate a key.
+ /// The salt used to randomize the key derivation.
+ /// The number of iterations to perform.
+ /// The desired byte length of the derived key.
+ /// A key derived from the provided password.
+ /// For compatibility with the Rfc2898DeriveBytes class, specify "SHA1" for the algorithmName parameter.
+ public unsafe static byte[] DeriveKey(string algorithmName, byte[] password, byte[] salt, ulong iterationCount, uint numBytesToDerive)
+ {
+ if (String.IsNullOrEmpty(algorithmName))
+ {
+ throw new ArgumentException(Res.Common_NullOrEmpty, "algorithmName");
+ }
+ if (password == null || password.Length == 0)
+ {
+ throw new ArgumentException(Res.Common_NullOrEmpty, "password");
+ }
+ if (salt == null || salt.Length == 0)
+ {
+ throw new ArgumentException(Res.Common_NullOrEmpty, "salt");
+ }
+ if (iterationCount <= 0)
+ {
+ throw new ArgumentOutOfRangeException("iterationCount");
+ }
+
+ byte[] derivedKey = new byte[numBytesToDerive];
+ int status;
+
+ using (BCryptAlgorithmHandle algHandle = Algorithms.CreateGenericHMACHandleFromPrimitiveProvider(algorithmName))
+ {
+ fixed (byte* pPassword = password)
+ fixed (byte* pSalt = salt)
+ fixed (byte* pDerivedKey = derivedKey)
+ {
+ status = UnsafeNativeMethods.BCryptDeriveKeyPBKDF2(
+ algHandle, pPassword, (uint)password.Length, pSalt, (uint)salt.Length, iterationCount,
+ pDerivedKey, numBytesToDerive, dwFlags: 0);
+ }
+ }
+
+ if (status == 0 /* STATUS_SUCCESS */)
+ {
+ return derivedKey;
+ }
+ else
+ {
+ throw new CryptographicException(status);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Security.DataProtection/UnsafeNativeMethods.cs b/src/Microsoft.AspNet.Security.DataProtection/UnsafeNativeMethods.cs
index 88e8eb961a..086de32397 100644
--- a/src/Microsoft.AspNet.Security.DataProtection/UnsafeNativeMethods.cs
+++ b/src/Microsoft.AspNet.Security.DataProtection/UnsafeNativeMethods.cs
@@ -46,6 +46,19 @@ namespace Microsoft.AspNet.Security.DataProtection
[Out] out uint pcbResult,
[In] BCryptEncryptFlags dwFlags);
+ [DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd433795(v=vs.85).aspx
+ internal static extern int BCryptDeriveKeyPBKDF2(
+ [In] BCryptAlgorithmHandle hPrf,
+ [In] byte* pbPassword,
+ [In] uint cbPassword,
+ [In] byte* pbSalt,
+ [In] uint cbSalt,
+ [In] ulong cIterations,
+ [In] byte* pbDerivedKey,
+ [In] uint cbDerivedKey,
+ [In] uint dwFlags);
+
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375399(v=vs.85).aspx
internal static extern int BCryptDestroyHash(