«1. Введение

В этом кратком руководстве мы познакомимся с java.security.SecureRandom — классом, предоставляющим криптографически стойкий генератор случайных чисел.

2. Сравнение с java.util.Random

Стандартные реализации JDK java.util.Random используют алгоритм Linear Congruential Generator (LCG) для предоставления случайных чисел. Проблема этого алгоритма в том, что он не является криптографически стойким. Другими словами, сгенерированные значения гораздо более предсказуемы, поэтому злоумышленники могут использовать их для компрометации нашей системы.

Чтобы решить эту проблему, мы должны использовать java.security.SecureRandom в любых решениях по безопасности. Он создает криптографически стойкие случайные значения с помощью криптографически стойкого генератора псевдослучайных чисел (CSPRNG).

Чтобы лучше понять разницу между LCG и CSPRNG, посмотрите на приведенную ниже диаграмму, на которой представлено распределение значений для обоих алгоритмов:

3. Генерация случайных значений

Наиболее распространенный способ использования SecureRandom должен генерировать значения int, long, float, double или boolean:

Для генерации значений int мы можем передать верхнюю границу в качестве параметра:

int randomInt = secureRandom.nextInt();
long randomLong = secureRandom.nextLong();
float randomFloat = secureRandom.nextFloat();
double randomDouble = secureRandom.nextDouble();
boolean randomBoolean = secureRandom.nextBoolean();

Кроме того, мы можем генерировать поток значения для int, double и long:

int randomInt = secureRandom.nextInt(upperBound);

Для всех потоков мы можем явно установить размер потока:

IntStream randomIntStream = secureRandom.ints();
LongStream randomLongStream = secureRandom.longs();
DoubleStream randomDoubleStream = secureRandom.doubles();

а также исходные (включительно) и связанные (исключающие) значения:

IntStream intStream = secureRandom.ints(streamSize);

~ ~~ Мы также можем сгенерировать последовательность случайных байтов. Функция nextBytes() берет предоставленный пользователем массив байтов и заполняет его случайными байтами:

IntStream intStream = secureRandom.ints(streamSize, originValue, boundValue);

4. Выбор алгоритма

byte[] values = new byte[124];
secureRandom.nextBytes(values);

По умолчанию SecureRandom использует алгоритм SHA1PRNG для генерации случайных значений. Мы можем явно заставить его использовать другой алгоритм, вызвав метод getInstance():

Создание SecureRandom с оператором new эквивалентно SecureRandom.getInstance(“SHA1PRNG”).

SecureRandom secureRandom = SecureRandom.getInstance("NativePRNG");

Все генераторы случайных чисел, доступные в Java, можно найти на официальной странице документации.

5. Семена

Каждый экземпляр SecureRandom создается с начальным семенем. Он работает как база для предоставления случайных значений и изменяется каждый раз, когда мы генерируем новое значение.

Использование нового оператора или вызов SecureRandom.getInstance() позволит получить начальное значение по умолчанию из /dev/urandom.

Мы можем изменить начальное значение, передав его в качестве параметра конструктора:

или вызвав метод set для уже созданного объекта:

byte[] seed = getSecureRandomSeed();
SecureRandom secureRandom = new SecureRandom(seed);

Помните, что если мы создадим два экземпляра SecureRandom с одно и то же семя и одна и та же последовательность вызовов методов для каждого, они будут генерировать и возвращать идентичные последовательности чисел.

byte[] seed = getSecureRandomSeed();
secureRandom.setSeed(seed);

6. Заключение

В этом уроке мы узнали, как работает SecureRandom и как его использовать для генерации случайных значений.

Как всегда, весь код, представленный в этом руководстве, можно найти на GitHub.

«