简体   繁体   English

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

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

I would like to hash passwords using PBKDF2 with a pepper and a salt in C#.我想在 C# 中使用带有胡椒和盐的 PBKDF2 散列密码。 I am a bit new to Cryptography, so feel free to correct me if I am wrong.我对密码学有点陌生,所以如果我错了,请随时纠正我。

I use the Rfc2898DeriveBytes Class because (according to other Stackoverflow users) bcrypt and other hash algorithms aren't natively supported and verified in C#, so it could pose a security threat.我使用 Rfc2898DeriveBytes 类是因为(根据其他 Stackoverflow 用户的说法)bcrypt 和其他哈希算法在 C# 中不受本机支持和验证,因此它可能构成安全威胁。 The purpose of this post isn't to start a discussing about which hashing algorithm is the best.这篇文章的目的不是开始讨论哪种散列算法是最好的。 > Bcrypt in C# Stackoverflow > C# Stackoverflow 中的 Bcrypt

My goal: Every password will get a random salt and pepper, the password will be hashed with a certain amount of iterations.我的目标:每个密码都会得到一个随机的盐和胡椒,密码将经过一定数量的迭代进行散列。

My question: Is it bad to have a bigger input size compared to the desired hash size and is my implementation correct?我的问题:与所需的散列大小相比,输入大小是否不好,我的实现是否正确?

  • Example: (PasswordInput (?) + Pepper (16 Bytes) + Salt (16 Bytes) > HashOutput (20 Bytes)示例: (PasswordInput (?) + Pepper (16 Bytes) + Salt (16 Bytes) > HashOutput (20 Bytes)

My code我的代码

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);
    }
}

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.盐和胡椒至少应使用 128 位(16 字节 > 16 个字符)。
  • At least 10.000 iterations should be used for the algorithm.该算法至少应使用 10.000 次迭代。

Research: Microsoft Rfc2898DeriveBytes , Example Code研究: Microsoft Rfc2898DeriveBytes示例代码

PBKDF2 (with SHA-1) - which the terribly named Rfc2898DeriveBytes implements - uses a repeated HMAC with the password - encoded to bytes - as key. PBKDF2(带有 SHA-1) - 可怕的Rfc2898DeriveBytes实现 - 使用重复的 HMAC 和密码 - 编码为字节 - 作为密钥。 Generally, HMAC simply performs a padding method on the input key (ipad and opad if you want to look it up).一般情况下,HMAC 只是对输入键(ipad 和 opad 如果要查找)执行填充方法。 This padding goes up to the input block size of the hash function.该填充达到哈希函数的输入块大小 However, let's look at the definition of HMAC in the HMAC RFC:但是,让我们看看 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).我们用 B 表示这些块的字节长度(对于上述所有哈希函数示例,B=64),用 L 表示哈希输出的字节长度(MD5 的 L=16,SHA-1 的 L=20 )。 The authentication key K can be of any length up to B, the block length of the hash function.认证密钥 K 可以是任意长度,最大为 B,即散列函数的块长度。 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.使用长于 B 字节的密钥的应用程序将首先使用 H 对密钥进行散列,然后将得到的 L 字节字符串用作 HMAC 的实际密钥。

In your case, if your encoded password is longer than 64 - 16 = 48 bytes (with 16 the pepper size) your HMAC may be slower.在您的情况下,如果您的编码密码长于 64 - 16 = 48 个字节(胡椒大小为 16),您的 HMAC可能会更慢。 A smart PBKDF2 function however could detect this and work around the issue by performing the initial hashing part only once.然而,智能 PBKDF2 函数可以检测到这一点并通过仅执行一次初始散列部分来解决该问题。

So if your password is over 48 byte then you could give some benefit to the attacker if:因此,如果您的密码超过 48 字节,那么您可以在以下情况下为攻击者提供一些好处:

  1. your implementation isn't that smart and你的实现不是那么聪明
  2. the implementation of the attacker is smart.攻击者的实现聪明的。

Note that the hash output size has nothing to do with this.请注意,哈希输出大小与此无关。 You could use PBKDF2 with SHA-512 - with a block size of 1024 bits rather than 512 bits for SHA-1 and SHA-256 - in case this is a problem.您可以将 PBKDF2 与 SHA-512 一起使用 - 块大小为 1024 位,而不是 SHA-1 和 SHA-256 的 512 位 - 以防出现问题。

The hash output size of the hash function within PBKDF2 (SHA-1 by default) does matter if you request more than the output size of bytes.如果您请求的字节数大于输出大小,则 PBKDF2 中散列函数的散列输出大小(默认为 SHA-1)确实很重要。 In that case you are also handing back advantage to an attacker.在这种情况下,您也将优势还给了攻击者。 Fortunately you're only asking for 16 bytes in hashSize (a variable name that you might want to change to passwordHashSize to avoid confusion).幸运的是,您只要求hashSize 16 个字节(您可能希望将其更改为passwordHashSize以避免混淆的变量名称)。


I understand that:我明白那个:

Oh dear ;)哦亲爱的 ;)

  • A hash isn't reversible.散列是不可逆的。

A cryptographic hash function and password hash function isn't reversible, other hash functions may be reversible.加密散列函数和密码散列函数不可逆,其他散列函数可能是可逆的。

  • A salt and pepper are added to increase security and prevent rainbow table attacks.添加盐和胡椒粉以提高安全性并防止彩虹桌攻击。

You only require a salt for that.你只需要盐。 A pepper prevents an attacker from guessing the password altogether, if it can be kept safe and if it is strong enough.胡椒可以防止攻击者完全猜出密码,前提它可以保证安全并且足够强大。

  • 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.盐是一个唯一的随机字符串,它不必是秘密的,可以与哈希值一起存储在数据库中。

That's correct.没错。

  • A pepper is not unique and it is used for every hash.胡椒不是唯一的,它用于每个哈希。 It is a secret and it isn't stored in the database.这是一个秘密,它不存储在数据库中。

Or it is encrypted itself and stored in the database, but yeah, in the end it needs to be secured one way or the other.或者它本身被加密并存储在数据库中,但是是的,最终需要以一种或另一种方式保护它。

  • At least a 128-bit (16 bytes > 16 characters) should be used for the salt and pepper.盐和胡椒至少应使用 128 位(16 字节 > 16 个字符)。

Well, it's kind-of the upper limit, I'd say 64 to 128 fully random bits, preferably over 80. However, not every character can be mapped to a byte, so your pepper is unlikely to be fully random - a bad idea for something that is basically a secret key .嗯,这是上限,我会说 64 到 128 个完全随机的位,最好超过 80。但是,不是每个字符都可以映射到一个字节,所以你的胡椒不太可能是完全随机的 - 一个坏主意对于基本上是秘密密钥的东西。

Note that the salt configuration option of PBKDF2 may be any size up to 64 - 8 - 4 = 52 bytes before another block encrypt is needed for SHA-1.请注意,在 SHA-1 需要另一个块加密之前,PBKDF2 的 salt 配置选项可以是最大 64 - 8 - 4 = 52 字节的任何大小。 For that reason the salt & pepper are usually concatenated.出于这个原因,盐和胡椒通常是连接在一起的。 This will also allow you use a true random pepper.这也将允许您使用真正的随机胡椒。 It leaves more characters (64) for the password as well that way.它也会以这种方式为密码留下更多字符 (64)。

  • At least 10.000 iterations should be used for the algorithm.该算法至少应使用 10.000 次迭代。

Commonly we recommend about a million nowadays.现在我们通常推荐大约一百万。 But really, any amount of CPU cycles you can spare makes it harder to an adversary.但实际上,您可以节省的任何 CPU 周期数量都会让对手更难。 The point is bit moot if the adversary really cannot get to the pepper of course.当然,如果对手真的无法获得胡椒,这一点就没有实际意义了。 In that case a single round is enough - but you may want to use a higher iteration count as a second line of defense (eg against sysadmins trying to get to passwords of your users, using a copy of the database).在这种情况下,单轮就足够了 - 但您可能希望使用更高的迭代次数作为第二道防线(例如,防止系统管理员尝试使用数据库副本获取用户密码)。

  1. Having an input bigger than the output size is not a problem.输入大于输出大小不是问题。 A good hash function should resist all attacks, even if the input is really big.一个好的哈希函数应该能够抵御所有攻击,即使输入非常大。
  2. Speaking of good hash functions: The output size of 20 bytes hints at SHA-1 being used.说到好的散列函数:20 字节的输出大小暗示正在使用 SHA-1。 SHA-1 is so broken that practical collisions already exist so it should not be used. SHA-1 已被破坏,实际冲突已经存在,因此不应使用它。 (I don't know if Rfc2898 is a scheme that is secure if collisions for SHA-1 exist or not, but one is better safe than sorry when it comes to security. (我不知道 Rfc2898 是否是一种安全方案,无论是否存在 SHA-1 冲突,但在安全性方面,安全总比抱歉好。
  3. The implementation seems to be ok otherwise.否则,实施似乎没问题。 I'm not up-to-date to C# but you seem to be using a cryptographic RNG for the salt.我不是最新的 C#,但您似乎正在使用加密 RNG 作为盐。
  4. You pepper should also be random.你的胡椒也应该是随机的。
  5. 128 bit (16 byte) keys are totally fine as long you're passwords don't need to be kept secure for longer than 50 years or so. 128 位(16 字节)密钥完全没问题,只要您的密码不需要保持安全超过 50 年左右。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM