使用 Rfc2898DeriveBytes 在 C# 中用胡椒和盐散列密码

Hashing a password with pepper and salt in C# using Rfc2898DeriveBytes

我想在 C# 中使用 PBKDF2 和胡椒粉和盐对密码进行哈希处理。我对密码学有点陌生,如果我错了,请随时纠正我。

我使用 Rfc2898DeriveBytes Class 因为(根据其他 Whosebug 用户的说法)bcrypt 和其他哈希算法在 C# 中不受本地支持和验证,因此它可能构成安全威胁。此 post 的目的不是开始讨论哪种哈希算法是最好的。 > Bcrypt in C# Whosebug

我的目标: 每个密码都会得到随机的盐和胡椒粉,密码会经过一定的迭代次数进行哈希处理。

我的问题:与所需的哈希大小相比,输入大小是否不好?我的实现是否正确?

我的代码

public class GenerateHash
{
    //Fields
    private const int saltSize = 16;
    private const int hashSize = 16;
    private const int iterations = 10000;
    private const string secretPepper = "Secret 16 Byte pepper."; 

   //Properties
    private string inputId { get; set; }

    //Methods
    public byte[] GeneratePBKDF2String(string inputId, string secretPepper, int saltSize, int 
    hashSize, int iterations)
    {
        // Generate a random salt.
        RNGCryptoServiceProvider cryptographicServiceProvider = new RNGCryptoServiceProvider();
        byte[] salt = new byte[saltSize];
        provider.GetBytes(salt);

        // Generate a salted hash with pepper.
        Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(inputId + secretPepper, salt, iterations);
        return pbkdf2.GetBytes(hashSize);
    }
}

我明白了:

研究: Microsoft Rfc2898DeriveBytes, Example Code

  1. 输入大于输出大小不是问题。一个好的哈希函数应该能抵抗所有的攻击,即使输入真的很大。
  2. 说到好的散列函数:20 字节的输出大小暗示正在使用 SHA-1。 SHA-1 已损坏到实际冲突已经存在,因此不应使用它。 (我不知道 Rfc2898 是否是一种在 SHA-1 冲突存在与否时安全的方案,但在安全性方面,安全总比遗憾好。
  3. 否则执行似乎没问题。我不是最新的 C#,但你似乎在使用加密 RNG 作为盐。
  4. 你辣椒也应该是随机的
  5. 128 位(16 字节)密钥完全可以,只要您的密码不需要安全保存超过 50 年左右。

PBKDF2(带有 SHA-1)——名字可怕的 Rfc2898DeriveBytes 实现的——使用重复的 HMAC 密码——编码为字节——作为密钥。通常,HMAC 只是简单地对输入键执行填充方法(ipad 和 opad,如果你想查找的话)。此填充增加到哈希函数的 输入块大小 。不过,让我们看看HMAC RFC中对HMAC的定义:

We denote by B the byte-length of such blocks (B=64 for all the above mentioned examples of hash functions), and by L the byte-length of hash outputs (L=16 for MD5, L=20 for SHA-1). The authentication key K can be of any length up to B, the block length of the hash function. Applications that use keys longer than B bytes will first hash the key using H and then use the resultant L byte string as the actual key to HMAC.

在您的情况下,如果您的编码密码长于 64 - 16 = 48 字节(胡椒大小为 16),您的 HMAC 可能 更慢。 然而,智能 PBKDF2 函数可以检测到这一点并通过仅执行一次初始散列部分来解决该问题。

因此,如果您的密码超过 48 字节,那么您可以给攻击者一些好处,如果:

  1. 您的实现不是那么聪明并且
  2. 攻击者的实施聪明。

请注意,哈希输出大小与此无关。您可以将 PBKDF2 与 SHA-512 一起使用 - 块大小为 1024 位而不是 SHA-1 和 SHA-256 的 512 位 - 以防出现问题。

如果您请求超过字节输出大小,PBKDF2(默认为 SHA-1)中散列函数的散列输出大小很重要。在那种情况下,您也将优势还给了攻击者。幸运的是,您只需要 hashSize 中的 16 个字节(您可能希望将变量名称更改为 passwordHashSize 以避免混淆)。


I understand that:

哦亲爱的 ;)

  • A hash isn't reversible.

加密哈希函数和密码哈希函数不可逆,其他哈希函数可能是可逆的。

  • A salt and pepper are added to increase security and prevent rainbow table attacks.

你只需要盐。胡椒粉可以完全防止攻击者猜测密码,如果它可以保持安全并且足够强大。

  • A salt is a unique and random string, it doesn't have to be secret and can be stored alongside the hash in a database.

没错。

  • A pepper is not unique and it is used for every hash. It is a secret and it isn't stored in the database.

或者它本身被加密并存储在数据库中,但是,是的,最终它需要以一种或另一种方式进行保护。

  • At least a 128-bit (16 bytes > 16 characters) should be used for the salt and pepper.

嗯,有点上限,我说64到128个完全随机位,最好是80个以上。但是,不是每个字符都可以映射到一个字节,所以你的胡椒不太可能是完全随机的随机 - 对于基本上是 密钥 .

的东西来说是个坏主意

请注意,在 SHA-1 需要另一个块加密之前,PBKDF2 的 salt 配置选项可以是任何大小,最大为 64 - 8 - 4 = 52 字节。出于这个原因,盐和胡椒通常是串联的。这也将允许您使用真正的随机胡椒。这样也可以为密码留下更多字符 (64)。

  • At least 10.000 iterations should be used for the algorithm.

现在一般推荐一百万左右。但实际上,任何数量的 CPU 周期都可以让对手更难应对。当然,如果对手 真的 无法拿到胡椒粉,那么这一点就没有实际意义了。在那种情况下,一轮就足够了——但您可能希望使用更高的迭代次数作为第二道防线(例如,防止系统管理员使用数据库的副本试图获取您用户的密码)。