4

因此,我最近为密码相关方法创建了一个静态类,并且必须创建一个生成安全盐的方法。

最初,我实现RNGCryptoServiceProvider并将 n 个字节归档到一个数组中,然后将其转换为 base64 并返回。

问题在于输出长度在转换后当然比 n 长(这是有道理的)。

为了解决这个问题,我将函数更改为下面的方法,我只是想知道通过修剪 base64 字符串是否会引发任何安全风险?

/// <summary>
/// Generates a salt for use with the Hash method.
/// </summary>
/// <param name="length">The length of string to generate.</param>
/// <returns>A cryptographically secure random salt.</returns>
public static string GenerateSalt(int length)
{
    // Check the length isn't too short.
    if (length < MIN_LENGTH)
    {
        throw new ArgumentOutOfRangeException("length", "Please increase the salt length to meet the minimum acceptable value of " + MIN_LENGTH + " characters.");
    }

    // Calculate the number of bytes required.
    // https://en.wikipedia.org/wiki/Base64#Padding
    // http://stackoverflow.com/questions/17944/how-to-round-up-the-result-of-integer-division
    int bytelen = ((3 * length) + 4 - 1) / 4;

    // Create our empty salt array.
    byte[] bytes = new byte[bytelen];

    // Where we'll put our generated salt.
    string salt;

    // Generate a random secure salt.
    using (RNGCryptoServiceProvider randcrypto = new RNGCryptoServiceProvider())
    {
        // Fill our array with random bytes.
        randcrypto.GetBytes(bytes);

        // Get a base64 string from the random byte array.
        salt = GetBase64(bytes);
    }

    // Trim the end off only if we need to.
    if (salt.Length > length)
    {
        // Substring is the fastest method to use.
        salt = salt.Substring(0, length);
    }

    // Return the salt.
    return salt;
}

另外作为一个附带问题,我快速浏览了一下,实际上无法找到C#实现的哈希函数RNGCryptoServiceProvider实际上是什么。有人知道随手吗?

4

3 回答 3

3

Why is the length of the salt so important to you? I wouldn't think that there are any real security implication, since the only real requirement of a salt is that it be random and unguessable.

In other words, go for it.

EDIT: Here is another way of doing it using Linq.

Random random = new Random();
int length = 25; // Whatever length you want
char[] keys = "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890!£$%^&*()".ToCharArray(); // whatever chars you want
var salt = Enumerable
    .Range(1, length) // equivalent to the loop bit, for(i.. ) 
    .Select(k => keys[random.Next(0, keys.Length - 1)])  // generate a new random char 
    .Aggregate("", (e, c) => e + c); // join them together into a string
于 2012-08-22T08:31:21.450 回答
2

There is no security risk with that way of generating the salt.

The salt doesn't need that level of security at all, it's just there so that rainbow tables can't be used to crack the hash/encryption. The regular Random class would be enough to create a salt.

Example:

/// <summary>
/// Generates a salt for use with the Hash method.
/// </summary>
/// <param name="length">The length of string to generate.</param>
/// <returns>A random salt.</returns>
public static string GenerateSalt(int length) {
    // Check the length isn't too short.
    if (length < MIN_LENGTH) {
        throw new ArgumentOutOfRangeException("length", "Please increase the salt length to meet the minimum acceptable value of " + MIN_LENGTH + " characters.");
    }

    // Where we'll put our generated salt.
    StringBuilder salt = new StringBuilder(length);

    // Fill our string with random characters.
    Random rnd = new Random();
    string chars = "0123456798ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    for (int i = 0; i < length; i++) {
      salt.Append(chars[rnd.Next(chars.Length)]);
    }

    // Return the salt.
    return salt.ToString();
}

Note: If the function would be used more than once close in time, you would use a single Random object and pass into the function, as Random instances created too close in time will give the same random sequence.

于 2012-08-22T08:31:26.533 回答
1

只是为了好玩,这是一种更快的方法(即使代码看起来不太好)。尝试剪切并粘贴它以查看。在我的机器上,它的执行时间约为 1.6 秒,而 7.1 秒。由于我在每种情况下都进行了一百万次迭代,所以我认为执行时间并不那么重要!

string msg = "";
int desiredLength = 93; // Length of salt required
Stopwatch watch = new Stopwatch();
watch.Start();
for (int k=0; k<1000000; k++)
{
    double guidsNeeded = Math.Ceiling(desiredLength / 36.0);
    string salt = "";
    for (int i=0; i<guidsNeeded; i++)
    {
       salt += Guid.NewGuid().ToString();
    }
    salt = salt.Substring(0,desiredLength);
}
msg += watch.ElapsedMilliseconds.ToString(); // 1654 ms

watch.Start();
for (int j=0; j<1000000; j++)
{
    GenerateSalt(93);
}
msg += "\r\n" + watch.ElapsedMilliseconds.ToString(); // 7096 ms

这是使用 Guffa 的代码GenerateSalt

于 2012-08-22T09:20:11.740 回答