获取独特的盐进行加密

发布于 2025-02-02 01:03:10 字数 382 浏览 3 评论 0 原文

我们的要求是用独特的盐存储每个密码。

查看他们的旧代码,我看到它使用

该文档说“填充了一系列字节,具有一个随机非零值的密码强的序列。”

并不是说盐是独一无二的。

我应该根据一天中的时间生成一些数据吗?也许是GUID?还是这些组合?

有什么建议吗?

Our requirement is to store each password with a unique salt.

Looking at their old code, I see it uses RNGCryptoServiceProvider.GetNonZeroBytes.

The documentation says that it "Fills an array of bytes with a cryptographically strong sequence of random nonzero values."

That does not say that the salt is unique, though, does it.

Should I generate some data based on the time of day? Maybe a GUID? Or a combination of these?

Any suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

乖乖 2025-02-09 01:03:11

使用评论中的建议,我将从此开始:

public const int IV_SIZE = 32;

public static byte[] GetRandomSalt()
{
    var array = new byte[IV_SIZE];
    using (var crypto = new RNGCryptoServiceProvider())
    {
        crypto.GetNonZeroBytes(array);
    }
    return array;
}

Using suggestions from the comments, I will start with this:

public const int IV_SIZE = 32;

public static byte[] GetRandomSalt()
{
    var array = new byte[IV_SIZE];
    using (var crypto = new RNGCryptoServiceProvider())
    {
        crypto.GetNonZeroBytes(array);
    }
    return array;
}
允世 2025-02-09 01:03:11

您想要32个字节独特的盐。为了保证的唯一性,您需要一个密码,而不是统计上的唯一性。由于密码是一对一的,因此,如果使用相同键加密唯一的输入,则将获得唯一的输出。因此,加密计数器0、1、2、3,...将为您提供独特的输出。

32个字节为256位。对于块密码来说,这很大,例如,AES为128位。您没有说您需要多少个独特的盐。如果您需要少于2^128盐,则只需使用一个AES块,并添加了16个字节。否则,您将需要两个AES块。这需要将AES输入扩展到256位,并带有领先的零,并期望与AES返回两个街区。

128位AES +随机伪代码:

global int counter  // 128 bit integer
global array key[]

generateUniqueSalt_v1()
{
  array saltPartOne[] <- AES.encrypt.ECB(counter, key)
  array saltPartTwo[] <- getRandomBytes(16)
  array salt[] <- saltPartOne concatenate saltPartTwo
  counter <- counter + 1
  return salt
}

256位AES伪代码:

global int counter  // 256 bits if possible.
global array key[]

generateUniqueSalt_v2()
{
  array plaintext[] <- counter extended to 256 bits if needed
  array salt[] <- AES.encrypt.ECB(plaintext, key)
  counter <- counter + 1
  return salt
}

You want a 32 byte unique salt. For guaranteed uniqueness, rather than statistically highly probably uniqueness, you need a cipher. Because ciphers are one-to-one, if you encrypt unique inputs with the same key then you will get unique outputs. Hence encrypting a counter 0, 1, 2, 3, ... will give you unique outputs.

32 bytes is 256 bits. That is large for a block cipher, AES is 128 bits for example. You do not say how many unique salts you need. If you need less than 2^128 salts, then you only need to use one AES block, with 16 bytes of random added. Otherwise you will need two AES blocks. That requires extending the AES input to 256 bits with leading zeros and expecting a two block return from AES.

128 bit AES + random pseudocode:

global int counter  // 128 bit integer
global array key[]

generateUniqueSalt_v1()
{
  array saltPartOne[] <- AES.encrypt.ECB(counter, key)
  array saltPartTwo[] <- getRandomBytes(16)
  array salt[] <- saltPartOne concatenate saltPartTwo
  counter <- counter + 1
  return salt
}

256 bit AES pseudocode:

global int counter  // 256 bits if possible.
global array key[]

generateUniqueSalt_v2()
{
  array plaintext[] <- counter extended to 256 bits if needed
  array salt[] <- AES.encrypt.ECB(plaintext, key)
  counter <- counter + 1
  return salt
}
dawn曙光 2025-02-09 01:03:10

适当的密码障碍库通常自己生成盐,并将其直接包含在哈希弦中,这是推荐的方法。一个很好的例子是bcrypt:

// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
string hashToStoreInDb = BCrypt.HashPassword(password);

// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from existingHashFromDb.
bool isPasswordCorrect = BCrypt.Verify(password, existingHashFromDb);

如果您需要单独生成盐,通常的方法是像您在示例中一样从OS的随机来源读取随机字节,或者如本示例所示dotnet文档

对于每个密码,盐应该是唯一的,以防止可以将彩虹表用于多个密码。使用长时间的随机盐,机会极小很小,您获得了两次相同的盐。在最坏的情况下,攻击者可以使用彩虹表作为两个密码而不是一个密码,蛮力仍然会更快。

Appropriate password-hash libraries usually generate the salt on their own, and include it directly into the hash-string, this is the recommended approach. A good example is BCrypt: https://www.nuget.org/packages/BCrypt.Net-Next/

// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
string hashToStoreInDb = BCrypt.HashPassword(password);

// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from existingHashFromDb.
bool isPasswordCorrect = BCrypt.Verify(password, existingHashFromDb);

If you need to generate the salt on your own, the usual way is to read random bytes from the random source of the OS as you did in your example, or as is shown in this example from the DotNet documentation Rfc2898DeriveBytes.

The salt should be unique for each password, to prevent that a rainbow table can be used for more than one password. With a long random salt the chance is extremely small that you get the same salt twice. In the worst case, an attacker could use a rainbow table for two passwords instead of one, brute-forcing will still be faster.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文