问题
I need to update some code that is using the PBKDF2 implementation in .Net, Rfc2898DeriveBytes
to hash user credentials.
It is my understanding that this function uses SHA-1 under the hood. I need to update the underlying hashing algorithm of the systems password hashing to use SHA-256 (This is a client IT-SEC requirement).
Having done some reading it seems it is best practice to continue to to use a Key derivation function, however PBKDF2 doesn't allow you to dictate the algorithm is should use, which is obviously a problem for me.
Our system is using .NET 4.5.1 and currently is not an option to upgrade that and I am reasonably confident it is not an option to reference any new .NET core .dlls that I've heard contain a new implementation of PBKDF2 that allows you to specify your algorithm.
I want to avoid home made implementations at all cost,s as that's the 1st rule of Crypto-Club right?
Any guidance on what is best practice would be appreciated.
Thanks
回答1:
You can specify an algorithm now, msdn page
Note: Available since 4.7.2
The names are available in System.Security.Cryptography.HashAlgorithmName
回答2:
You can P/Invoke to BCryptDeriveKeyPBKDF2, assuming you're on Win7+.
private static void PBKDF2(
string password,
byte[] salt,
int iterationCount,
string hashName,
byte[] output)
{
int status = SafeNativeMethods.BCryptOpenAlgorithmProvider(
out SafeNativeMethods.SafeBCryptAlgorithmHandle hPrf,
hashName,
null,
SafeNativeMethods.BCRYPT_ALG_HANDLE_HMAC_FLAG);
using (hPrf)
{
if (status != 0)
{
throw new CryptographicException(status);
}
byte[] passBytes = Encoding.UTF8.GetBytes(password);
status = SafeNativeMethods.BCryptDeriveKeyPBKDF2(
hPrf,
passBytes,
passBytes.Length,
salt,
salt.Length,
iterationCount,
output,
output.Length,
0);
if (status != 0)
{
throw new CryptographicException(status);
}
}
}
[SuppressUnmanagedCodeSecurity]
private static class SafeNativeMethods
{
private const string BCrypt = "bcrypt.dll";
internal const int BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x00000008;
[DllImport(BCrypt, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern int BCryptDeriveKeyPBKDF2(
SafeBCryptAlgorithmHandle hPrf,
byte[] pbPassword,
int cbPassword,
byte[] pbSalt,
int cbSalt,
long cIterations,
byte[] derivedKey,
int cbDerivedKey,
int dwFlags);
[DllImport(BCrypt)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
private static extern int BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, int flags);
[DllImport(BCrypt, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern int BCryptOpenAlgorithmProvider(
out SafeBCryptAlgorithmHandle phAlgorithm,
string pszAlgId,
string pszImplementation,
int dwFlags);
internal sealed class SafeBCryptAlgorithmHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeBCryptAlgorithmHandle() : base(true)
{
}
protected override bool ReleaseHandle()
{
return BCryptCloseAlgorithmProvider(handle, 0) == 0;
}
}
}
回答3:
I'll tell you what I would do: I would take the source of the newest (not exactly the newest because it uses Span<>
... Just a little older :-) ) of Rfc2898DeriveBytes
from the corefx github
You'll need the full code of:
- https://github.com/dotnet/corefx/blob/29cb063b95661470340b6ba7e1381495c05bfff2/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/Rfc2898DeriveBytes.cs
- https://github.com/dotnet/corefx/blob/45b724f6b6391910edea8a70f3f22a4a7996696d/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithmName.cs
- https://github.com/dotnet/corefx/blob/bffef76f6af208e2042a2f27bc081ee908bb390b/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/Helpers.cs
plus two methods (GenerateRandom
and WriteInt
) from
- https://github.com/dotnet/corefx/blob/827f47f48df00923b802427486b062d62dd243b5/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Helpers.cs
Then you'll have some calls to SR.*something*
that you'll have to replace to some messages like "some error"
, plus a SR.Format
that you have to replace with string.Format
.
Then you'll have (nearly) the newest version of Rfc2898DeriveBytes
that has a constructor that accepts as a parameter HashAlgorithmName.SHA256
.
This should be the end result: https://ideone.com/lb2Qya
I had the bad idea of putting the source code in the namespace My.System
... bad bad idea... I had to prefix global::
to all the namespaces :-(
来源:https://stackoverflow.com/questions/50796527/pbkdf2-using-sha-256-in-net