如何在盐和哈希代码中使用 SHA-512 和 Rfc2898DeriveBytes?

问题描述 投票:0回答:4

我对密码学完全陌生,但正在学习。我从网上的研究中拼凑出许多不同的建议,并制作了自己的类来处理哈希、盐、密钥拉伸以及关联数据的比较/转换。

在研究了内置的.NET 密码学库后,我发现我拥有的仍然只是 SHA-1。但我得出的结论是,这还不错,因为我使用了哈希过程的多次迭代。这是正确的吗?

但是如果我想从更强大的 SHA-512 开始,我该如何在下面的代码中实现它呢?预先感谢。

using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;

public class CryptoSaltAndHash
{
    private string strHash;
    private string strSalt;
    public const int SaltSizeInBytes = 128;
    public const int HashSizeInBytes = 1024;
    public const int Iterations = 3000;

    public string Hash { get { return strHash; } }
    public string Salt { get { return strSalt; } }

    public CryptoSaltAndHash(SecureString ThisPassword)
    {
        byte[] bytesSalt = new byte[SaltSizeInBytes];
        using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
        {
            crypto.GetBytes(bytesSalt);
        }
        strSalt = Convert.ToBase64String(bytesSalt);
        strHash = ComputeHash(strSalt, ThisPassword);
    }

    public static string ComputeHash(string ThisSalt, SecureString ThisPassword)
    {
        byte[] bytesSalt = Convert.FromBase64String(ThisSalt);
        Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(
            convertSecureStringToString(ThisPassword), bytesSalt, Iterations);
        using (pbkdf2)
        {
            return Convert.ToBase64String(pbkdf2.GetBytes(HashSizeInBytes));
        }
    }

    public static bool Verify(string ThisSalt, string ThisHash, SecureString ThisPassword)
    {
        if (slowEquals(getBytes(ThisHash), getBytes(ComputeHash(ThisSalt, ThisPassword))))
        {
            return true;
        }
        return false;
    }

    private static string convertSecureStringToString(SecureString MySecureString)
    {
        IntPtr ptr = IntPtr.Zero;
        try
        {
            ptr = Marshal.SecureStringToGlobalAllocUnicode(MySecureString);
            return Marshal.PtrToStringUni(ptr);
        }
        finally
        {
            Marshal.ZeroFreeGlobalAllocUnicode(ptr);
        }
    }

    private static bool slowEquals(byte[] A, byte[] B)
    {
        int intDiff = A.Length ^ B.Length;
        for (int i = 0; i < A.Length && i < B.Length; i++)
        {
            intDiff |= A[i] ^ B[i];
        }
        return intDiff == 0;
    }

    private static byte[] getBytes(string MyString)
    {
        byte[] b = new byte[MyString.Length * sizeof(char)];
        System.Buffer.BlockCopy(MyString.ToCharArray(), 0, b, 0, b.Length);
        return b;
    }
}

注释:我参考了https://crackstation.net/hashing-security.htm的很多实践。 SlowEquals 比较方法是通过防止分支来标准化执行时间。 SecureString 的用途是在此类与 Web 应用程序中的其他类和页面之间传递密码的加密形式。虽然该网站将通过 HTTPS 进行传输,但在合理的情况下,多加努力以确保尽可能安全总是好的。

在我的代码中,我将密钥字符串设置为 128 字节(尽管有时会变大,这很好),哈希大小设置为 1KB,迭代次数设置为 3,000。它比典型的 64 字节盐、512 字节哈希和 1,000 或 2,000 次迭代稍大,但登录速度和应用程序性能的优先级极低。

想法?

c# hash cryptography salt-cryptography sha
4个回答
12
投票
  1. 3000 次迭代是相当低的。即使10000也很低。但是,您需要权衡额外迭代的安全增益与攻击者通过尝试频繁登录来攻击您的服务器的风险,这会为每次尝试触发昂贵的哈希值。
  2. 大于 128 位/16 字节的盐是没有意义的。盐应该是唯一的,仅此而已。
  3. 大于本机大小(SHA-1 为 20 字节)的哈希大小会降低防御者的性能,但不会降低攻击者的性能。由于这意味着您可以承担更少的迭代,因此它实际上削弱了安全性。

    例如,以与 3000 次迭代的 1024 字节哈希相同的成本,您可以负担得起 156000 次迭代的 20 字节哈希,这比破解成本高出 52 倍。

  4. 要使用 SHA-2,您需要完全不同的 PBKDF2 实现,.net 中包含的实现被硬编码为使用 SHA-1。

    如果您费心使用第三方库,我宁愿使用 bcrypt 库,因为它对于基于 GPU 的攻击者来说要强大得多。

  5. 您的 API 使用起来很尴尬,因为您将盐管理推给调用者,而不是在

    Create
    /
    Verify
    函数中处理它。

  6. 使用

    SecureString
    然后将其转换为
    String
    是愚蠢的。这抵消了首先使用
    SecureString
    的全部意义。

    就我个人而言,我不会在典型的应用程序中使用

    SecureString
    。只有将其与广泛的全栈安全审查结合起来才有价值,该审查会检查密码是否永远不会存储在
    String
    中,并且一旦不再需要,总是从可变存储中删除。

  7. 我不会将密码/盐存储在实例变量中。只需将它们保留在相关功能的本地即可。我只会将配置存储在实例变量中(例如迭代计数)。

  8. 虽然 SHA-1 在密码学上被削弱,但攻击会产生冲突。由于密码哈希冲突无关紧要,您首先关心的是原像攻击。 SHA-1 在这方面仍然相当强大。

    SHA-512 的主要优点不是它的加密性更强(尽管确实如此),而是 64 位算术使攻击者比防御者付出更多的代价,因为防御者可能会使用提供快速 64 位算术的 64 位 Intel CPU .


8
投票

如果有人通过搜索遇到这个问题,现在微软提供了 Microsoft.AspNetCore.Cryptography.KeyDerivation NuGet 包,它允许将 PBKDF2 与 SHA-256 和 SHA-512 哈希函数一起使用。文档可在 learn.microsoft.com 获取。


6
投票

回答问题:从“SecurityDriven.NET”书中下载免费代码示例。找到采用 HMAC 工厂的 PBKDF2 类。 HMACSHA512 工厂可用等。

由于您是密码学新手,我也强烈建议您阅读这本书(例如,充分理解 CodesInChaos 提出的观点)。


2
投票

对于那些通过搜索找到此内容的人,答案是

Rfc2898DeriveBytes
构造函数将哈希算法作为参数,而
HashAlgorithmName.SHA512
是一个受支持的选项。 所以,这有效:

var pbkdf2 = new Rfc2898DeriveBytes(passwordBytes, bytesSalt, 1000000, HashAlgorithmName.SHA512);
© www.soinside.com 2019 - 2024. All rights reserved.