PBKDF2 using SHA 256 in .NET

核能气质少年 提交于 2019-12-13 01:47:43

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!