6

我目前正在做的创建随机密码是这样的:

    public static int getRandomNumber(int maxNumber)
    {
        if (maxNumber < 1)
            throw new System.Exception("The maxNumber value should be greater than 1");
        byte[] b = new byte[4];
        new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(b);
        int seed = (b[0] & 0x7f) << 24 | b[1] << 16 | b[2] << 8 | b[3];
        System.Random r = new System.Random(seed);
        return r.Next(1, maxNumber);
    }

一些可能的问题?这是一个静态函数,有一些奇怪的种子模式,可能不安全,仍然使用 System.Random()。

使用上面的随机数生成器,我以这种低效的方式创建了一个字符串:

  validCharacters = "abcdefghjkmnoxyz023456789#!@";

然后循环并使用有效数组获取“createPassword(length)”类型的字符串(注意使用的字符集不包含容易出错的字符,如 1 i 等)。

这是怎么做,还是有更简单、更安全、更有效的方法

4

4 回答 4

8

为了生成随机数,您可以返回seed除以的余数maxNumber

public static int getRandomNumber(int maxNumber)
{
    if (maxNumber < 1)
        throw new System.Exception("The maxNumber value should be greater than 1");
    byte[] b = new byte[4];
    new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(b);
    int seed = (b[0] & 0x7f) << 24 | b[1] << 16 | b[2] << 8 | b[3];
    return seed % maxNumber;
}

maxNumber在这种情况下是排他的,这也是如何Random.Next(maxNumber)工作的。

编辑

@Servy 的评论非常有趣,让我看到了 Stephen Toub 和 Shawn Farkas 在 MSDN 杂志 2007 年 9 月号上发表的题为“来自 CryptoRandom 的故事”的文章,可在此处下载,其中有一个使用 RNGCryptoServiceProvider 重新实现 Random 的示例这有一个解决偏见的方法。我在这里包含了他们的实现,因为源代码的格式非常讨厌,但是这篇文章值得一读,因为它背后的原因。

public class CryptoRandom : Random
{
    private RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();
    private byte[] _uint32Buffer = new byte[4];
    public CryptoRandom() { }
    public CryptoRandom(Int32 ignoredSeed) { }
    public override Int32 Next()
    {
        _rng.GetBytes(_uint32Buffer);
        return BitConverter.ToInt32(_uint32Buffer, 0) & 0x7FFFFFFF;
    }
    public override Int32 Next(Int32 maxValue)
    {
        if (maxValue < 0) throw new ArgumentOutOfRangeException("maxValue");
        return Next(0, maxValue);
    }
    public override Int32 Next(Int32 minValue, Int32 maxValue)
    {
        if (minValue > maxValue) throw new ArgumentOutOfRangeException("minValue");
        if (minValue == maxValue) return minValue;
        Int64 diff = maxValue - minValue;
        while (true)
        {
            _rng.GetBytes(_uint32Buffer);
            UInt32 rand = BitConverter.ToUInt32(_uint32Buffer, 0);
            Int64 max = (1 + (Int64)UInt32.MaxValue);
            Int64 remainder = max % diff;
            if (rand < max - remainder)
            {
                return (Int32)(minValue + (rand % diff));
            }
        }
    }
    public override double NextDouble()
    {
        _rng.GetBytes(_uint32Buffer);
        UInt32 rand = BitConverter.ToUInt32(_uint32Buffer, 0);
        return rand / (1.0 + UInt32.MaxValue);
    }
    public override void NextBytes(byte[] buffer)
    {
        if (buffer == null) throw new ArgumentNullException("buffer");
        _rng.GetBytes(buffer);
    }
}
于 2014-03-11T22:24:38.557 回答
2

您的使用System.Random使得断言安全属性非常困难,因为它的输出在密码学上并不强。现在只有 40 亿个密码,因为只有 40 亿个可能的种子。

很不安全。由于类似的问题,Debian 密码安全性受到威胁。

直接用于RNGCryptoServiceProvider生成完全随机的整数作为密码算法的输入。

于 2014-03-11T21:20:47.697 回答
0

您可以使用 RNGCryptoServiceProvider 从 System.Random 继承来获取数字,但您必须小心,因为您需要覆盖很多方法:

/*
[MSDN]

Notes to Inheritors:

In the .NET Framework versions 1.0 and 1.1, a minimum implementation of
a class derived from Random required overriding the Sample method
to define a new or modified algorithm for generating random numbers.
The derived class could then rely on the base class implementation
of the Random.Next(), Random.Next(Int32), Random.Next(Int32, Int32),
NextBytes, and NextDouble methods to call the derived class implementation of the Sample method.

In the .NET Framework version 2.0 and later, the behavior of the Random.Next(),
Random.Next(Int32, Int32), and NextBytes methods have changed
so that these methods do not necessarily call the derived class implementation
of the Sample method.
As a result, classes derived from Random that target the .NET Framework 2.0
and later should also override these three methods.
*/
class CryptoRandom : System.Random {
    private RandomNumberGenerator rng;
    public CryptoRandom() {
        rng = new RNGCryptoServiceProvider();
    }
    public override int Next() {
        byte[] bytes = new byte[4];
        rng.GetBytes(bytes);
        return int.MaxValue & BitConverter.ToInt32(bytes, 0);
    }
    public override void NextBytes(byte[] b)
    {
        rng.GetBytes(b);
    }
    public override int Next(int lo, int hi){
        throw new Exception("TODO override (arc4random_uniform is beautiful)");
    }
    protected override double Sample()
    {
        throw new Exception("TODO override");
    }
}

或者,由于您将使用所有这些来获取一个小的(长度<=255)字符串的索引,您可以直接使用字节来获取其中的索引(避免模偏差,这就是我写这个答案的原因首先 - 请参阅 http://BXR.SU/OpenBSD/lib/libc/crypt/arc4random_uniform.c#arc4random_uniform 上的arc4random_uniform)。

于 2014-03-12T14:48:30.930 回答
0

You could just take the raw bytes and base-28 encode them using the validCharacters string as the "digits" of the base-28 number. You can use the code here https://stackoverflow.com/a/14110071/2076

于 2014-09-16T17:27:12.763 回答