Как я могу использовать 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#!@";

затем цикл и получение строки типа "createPassword(length)" с использованием допустимого массива (обратите внимание на использование набора символов, который не включает легко ошибочные символы, такие как 1 i и т. д.).

Это как это сделать, или есть более простой, безопасный и эффективный способ?

4 ответа

Решение

Для генерации случайных чисел вы можете вернуть остаток 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 был очень интересным и привел меня к статье Стивена Тауба и Шона Фаркаса под названием "Рассказы из CryptoRandom" в выпуске журнала MSDN за сентябрь 2007 года, доступной для загрузки здесь, где приведен пример переопределения Random с помощью RNGCryptoServiceProvider. это имеет обходной путь для предвзятости. Я включил их реализацию здесь, так как форматирование в источнике довольно неприятно, но статью стоит прочитать по причинам, стоящим за этим.

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

Ваше использование System.Random делает утверждение свойств безопасности очень трудным, потому что его вывод не является криптографически сильным. Сейчас существует только 4 миллиарда паролей, потому что есть только 4 миллиарда возможных начальных значений.

Очень небезопасно. Безопасность пароля Debian была нарушена из-за аналогичной проблемы.

использование RNGCryptoServiceProvider непосредственно для генерации совершенно случайных целых чисел в качестве входных данных для вашего алгоритма пароля

Вы можете просто взять необработанные байты и кодировать их с помощью base-28, используя строку validCharacters в качестве "цифр" числа base-28. Вы можете использовать код здесь /questions/28986427/kodirovka-base-n-bajtovogo-massiva/28986447#28986447

Вы можете наследовать от System.Random, используя RNGCryptoServiceProvider для получения чисел, но вам придется позаботиться о том, чтобы переопределить несколько методов:

/*
[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) строку, вы можете использовать байты напрямую, чтобы получить индекс в нее (избегая смещения по модулю, поэтому я написал этот ответ в первую очередь - см. arc4random_uniform по адресу http://BXR.SU/OpenBSD/lib/libc/crypt/arc4random_uniform.c#arc4random_uniform).

Другие вопросы по тегам