如何使用RNGCryptoServiceProvider根据我的首选字符创建随机密码?

时间:2014-03-11 20:56:08

标签: c# random rngcryptoserviceprovider

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

    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#!@";

然后循环并获得&#34; createPassword(长度)&#34;使用有效数组键入字符串(注意使用不包含容错字符的字符集,如1 i等)。

这是怎么做的,还是更容易,更安全,更有效

4 个答案:

答案 0 :(得分:7)

为了生成随机数,您可以返回余下的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在2007年9月的MSDN杂志上发表题为“来自CryptoRandom的故事”的文章,可以下载here,使用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);
    }
}

答案 1 :(得分:2)

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

非常不安全。由于类似的问题,Debian密码安全性受到了损害。

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

答案 2 :(得分: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。

答案 3 :(得分:0)

您可以使用原始字节和base-28使用validCharacters字符串对它们进行编码,作为base-28数字的“数字”。您可以在此处使用代码https://stackoverflow.com/a/14110071/2076