在.NET中使用SHA 256的PBKDF2

4
我需要更新一些使用.Net中的PBKDF2实现Rfc2898DeriveBytes来散列用户凭据的代码。 据我了解,该函数在内部使用SHA-1。我需要更新系统密码散列的基础哈希算法以使用SHA-256(这是客户端IT-SEC要求)。
经过一些阅读,似乎最佳实践是继续使用密钥派生函数,但是PBKDF2不允许您指定它应使用的算法,这显然对我造成了问题。
我们的系统正在使用.NET 4.5.1,目前升级不是一个选项,并且我相当有信心不能引用任何新的.NET Core .dll,因为我听说其中包含了一个新的PBKDF2实现,可让您指定您的算法。
我想尽一切办法避免自制实现,因为这是Crypto-Club的第一条规则,对吗?
非常感谢您提供任何最佳实践的指导。

如果您查看PBKDF2实现的源代码,可能会发现它使用操作系统生成凭据。然后,只需要创建自己的实现来使用更高版本的操作系统调用。也许在某些地方需要使用P/Invoke,然后你就完成了。 - Neil
1
如果您无法升级.NET(这确实是最佳选择),则可以尝试使用Bouncy Castle - Jeroen Mostert
1
我们的系统正在使用.NET 4.5.1,目前无法升级。然而,微软已于2016年1月12日结束了对.NET 4.5.1的支持。你们如何获取安全更新?请参考https://blogs.msdn.microsoft.com/dotnet/2015/12/09/support-ending-for-the-net-framework-4-4-5-and-4-5-1/。 - ta.speot.is
易于升级的当然是仍受支持的4.5.2版本。但这并不是解决此问题的方法。原地踏步和前进一样都存在风险。 - bommelding
@ta.speot.is:“针对不再受支持的.NET Framework版本的应用程序将无需重新定位或重新编译到较新的版本,因为它们可以在稍后的.NET Framework版本上运行。”有时候,重新定位您的应用程序并不是一个选项。至少不是我能做出决定的选项。 - Dave
如果您的客户足够注重安全,要求使用新的哈希算法,那么要求他们使用最新(也更安全)版本的.NET可能并不过分,因此您的应用程序所针对的目标平台不应该是一个问题。如果您的应用程序应该支持旧平台另外,而且您不想维护两个二进制文件(虽然您可以通过条件编译),它可以针对.NET 4.5.1,并使用运行时反射来查找新功能是否存在。像那样的代码很脆弱,很难编写。最终,安全问题将迫使升级。 - Jeroen Mostert
3个回答

3
你可以使用P/Invoke调用BCryptDeriveKeyPBKDF2函数,假设你正在使用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;
        }
    }
}

2
我告诉你我会做什么:我会拿最新的源代码(不是最新的,因为它使用了Span<>...稍微旧一点的版本 :-))从corefx github中获取Rfc2898DeriveBytes
你需要完整的代码:

从这里新增两个方法 (GenerateRandomWriteInt)。

然后你需要将一些调用SR.*something*的内容替换为像"some error"这样的消息,还有一个SR.Format需要替换为string.Format
接下来你将拥有(几乎)最新版本的Rfc2898DeriveBytes,它具有一个构造函数,该构造函数接受HashAlgorithmName.SHA256作为参数。
这应该是最终结果:https://ideone.com/lb2Qya 我把源代码放在命名空间My.System中...真是个坏主意...我不得不在所有命名空间前加上global:: :-(

1
你现在可以指定一个算法,msdn页面 注意:自4.7.2以来可用。
名称在System.Security.Cryptography.HashAlgorithmName中可用。

虽然我很感谢你的回答,但我在问题中明确表示升级.NET版本不是一个选项。 - Dave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接