1. Обзор
При запуске виртуальной машины Java (JVM) мы можем определить различные свойства, которые изменят поведение нашей JVM. Одним из таких свойств является java.security.egd.
В этом уроке мы рассмотрим, что это такое, как его использовать и какой эффект он дает.
2. Что такое java.security.egd
?
В качестве свойства JVM мы можем использовать java.security.egd
, чтобы повлиять на инициализацию класса SecureRandom .
Как и все свойства JVM, мы объявляем его с помощью параметра -D
в нашей командной строке при запуске JVM:
java -Djava.security.egd=file:/dev/urandom -cp . com.foreach.java.security.JavaSecurityEgdTester
Как правило, если мы используем Java 8 или более позднюю версию и работаем в Linux, то наша JVM по умолчанию будет использовать файл:/dev/urandom .
3. Какой эффект имеет java.security.egd
?
Когда мы делаем наш первый вызов для чтения байтов из SecureRandom
, мы заставляем его инициализировать и читать файл конфигурации java.security JVM
.
Этот файл содержит свойство securerandom.source
:
securerandom.source=file:/dev/random
Поставщики безопасности, такие как sun.security.provider.Sun
по умолчанию, считывают это свойство при инициализации .
Когда мы устанавливаем наше свойство JVM java.security.egd
, поставщик безопасности может использовать его для переопределения свойства, настроенного в securerandom.source
.
Вместе java.security.egd
и securerandom.source определяют
, какое устройство сбора энтропии
(EGD) будет использоваться в качестве основного источника начальных данных, когда мы используем SecureRandom
для генерации случайных чисел.
До Java 8 мы находим java.security
в $JAVA_HOME/jre/lib/security
, но в более поздних реализациях он находится в $JAVA_HOME/conf/security
.
Воздействует ли параметр egd
на какой- либо эффект, зависит от реализации поставщика безопасности.
4. Какие значения может принимать java.security.egd
?
Мы можем указать java.security.egd
в формате URL с такими значениями, как:
файл:/dev/случайный
файл: /dev/urandom
файл:/dev/./urandom
Имеет ли этот параметр какой-либо эффект или какое-либо другое значение имеет значение, зависит от используемой платформы и версии Java, а также от того, как настроена безопасность нашей JVM.
В операционных системах (ОС) на базе Unix /dev/random
— это специальный путь к файлу, который отображается в файловой системе как обычный файл, но чтение из него фактически взаимодействует с драйвером устройства ОС для генерации случайных чисел. Некоторые реализации устройств также предоставляют доступ через /dev/urandom
и даже /dev/arandom
URI.
5. Что такого особенного в файле:/dev/./urandom
?
Во-первых, давайте поймем разницу между файлами /dev/random
и /dev/urandom:
/dev/random
собирает энтропию из разных источников;/dev/random
будет блокироваться до тех пор, пока у него не будет достаточно энтропии, чтобы удовлетворить наш запрос на чтение с непредсказуемыми данными./dev/urandom
будет извлекать псевдослучайность из всего, что доступно, без блокировки.
Когда мы впервые используем SecureRandom
, инициализируется наш Sun SeedGenerator
по умолчанию .
Когда мы используем какое-либо из специальных значений file:/dev/random
или file:/dev/urandom
, мы заставляем Sun SeedGenerator
использовать собственную (платформенную) реализацию.
Реализации провайдера в Unix могут блокироваться, продолжая читать из /dev/random
. В Java 1.4 была обнаружена эта проблема в некоторых реализациях. Впоследствии ошибка была исправлена в соответствии с предложением по улучшению JDK для Java 8 (JEP 123) .
Использование URL-адреса, такого как file:/dev/./urandom
или любого другого значения, заставляет SeedGenerator
рассматривать его как URL-адрес исходного источника, который мы хотим использовать .
В Unix-подобных системах наш URL-адрес file:/dev/./urandom
разрешается в тот же неблокирующий файл /dev/urandom
.
Однако мы не всегда хотим использовать это значение. В Windows у нас нет этого файла, поэтому наш URL-адрес не разрешается. Это запускает последний механизм генерации случайности и может задержать нашу инициализацию примерно на 5 секунд.
6. Эволюция SecureRandom
Эффект java.security.egd
изменился в различных выпусках Java.
Итак, давайте посмотрим на некоторые наиболее важные события, повлиявшие на поведение SecureRandom
:
Ява 1.4
Проблема блокировки /dev/random, поднятая в JDK-4705093: используйте /dev/urandom, а не /dev/random, если он существует
Ява 5
Исправление для JDK-4705093
Добавляет алгоритм
NativePRNG
для соблюдения параметраjava.security.egd
, но нам нужно настроить его вручную .Если используется
SHA1PRNG
, то он может заблокироваться, если мы используем что-либо кромеfile:/dev/urandom.
Другими словами, он может заблокироваться, если мы используемфайл:/dev/./urandom
Ява 8
Добавляет новую реализацию
SecureRandom
, которая учитывает свойства безопасностиДобавляет новый метод
getInstanceStrong()
для собственных надежных случайных чисел платформы. Отлично подходит для создания ценных и долгоживущих секретов, таких как пары закрытый/открытый ключ RSA.
Ява 9
Реализует три механизма генератора детерминированных случайных битов (DRBG), как описано в Рекомендации по генерации случайных чисел с использованием генераторов детерминированных случайных битов.
Понимание того, как изменился SecureRandom
, дает нам представление о вероятном влиянии свойства java.security.egd
.
7. Тестирование эффекта java.security.egd
Лучший способ убедиться в эффекте свойства JVM — попробовать его. Итак, давайте посмотрим на эффект java.security.egd
, запустив некоторый код для создания нового SecureRandom
и замерив время, необходимое для получения `` некоторых случайных байтов.
Во-первых, давайте создадим класс JavaSecurityEgdTester с методом
main()
. Мы установим время нашего вызова secureRandom.nextBytes()
с помощью System.nanoTime()
и отобразим результаты:
public class JavaSecurityEgdTester {
public static final double NANOSECS = 1000000000.0;
public static void main(String[] args) {
SecureRandom secureRandom = new SecureRandom();
long start = System.nanoTime();
byte[] randomBytes = new byte[256];
secureRandom.nextBytes(randomBytes);
double duration = (System.nanoTime() - start) / NANOSECS;
System.out.println("java.security.egd = " + System.getProperty("java.security.egd") + " took " + duration + " seconds and used the " + secureRandom.getAlgorithm() + " algorithm");
}
}
Теперь давайте запустим тест JavaSecurityEgdTester
, запустив новый экземпляр Java и указав значение для свойства java.security.egd
:
java -Djava.security.egd=file:/dev/random -cp . com.foreach.java.security.JavaSecurityEgdTester
Давайте проверим вывод, чтобы увидеть, сколько времени занял наш тест и какой алгоритм использовался:
java.security.egd=file:/dev/random took 0.692 seconds and used the SHA1PRNG algorithm
Поскольку наше системное свойство читается только при инициализации, давайте запустим наш класс в новой JVM для каждого другого значения java.security.egd
:
java -Djava.security.egd=file:/dev/urandom -cp . com.foreach.java.security.JavaSecurityEgdTester
java -Djava.security.egd=file:/dev/./urandom -cp . com.foreach.java.security.JavaSecurityEgdTester
java -Djava.security.egd=foreach -cp . com.foreach.java.security.JavaSecurityEgdTester
В Windows, использующей Java 8 или Java 11, тесты со значениями file:/dev/random
или file:/dev/urandom
дают меньше секунды. Использование чего-либо еще, например, file:/dev/./urandom
или даже foreach
, делает наши тесты более 5 секунд!
Смотрите наш предыдущий раздел для объяснения того, почему это происходит. В Linux мы можем получить другие результаты.
8. Как насчет SecureRandom.getInstanceStrong()
?
В Java 8 появился метод SecureRandom.getInstanceStrong()
. Посмотрим, как это повлияет на наши результаты.
Во-первых, давайте заменим наш новый SecureRandom()
на SecureRandom.
получитьInstanceStrong()
:
SecureRandom secureRandom = SecureRandom.getInstanceStrong();
Теперь давайте снова запустим тесты:
java -Djava.security.egd=file:/dev/random -cp . com.foreach.java.security.JavaSecurityEgdTester
При запуске в Windows значение свойства java.security.egd
не оказывает заметного влияния, когда мы используем SecureRandom.getInstanceStrong()
. Даже нераспознанное значение дает нам быстрый ответ.
Давайте снова проверим наш вывод и заметим время менее 0,01 секунды. Давайте также заметим, что алгоритм теперь Windows-PRNG:
java.security.egd=foreach took 0.003 seconds and used the Windows-PRNG algorithm
Обратите внимание, что PRNG в названиях алгоритмов означает генератор псевдослучайных чисел.
9. Заполнение алгоритма
Поскольку случайные числа широко используются в криптографии для ключей безопасности, они должны быть непредсказуемыми.
Таким образом, то, как мы задаем наши алгоритмы, напрямую влияет на предсказуемость случайных чисел, которые они производят.
Чтобы создать непредсказуемость, реализации SecureRandom
используют энтропию, собранную из накопленных входных данных, для заполнения своих алгоритмов. Это происходит от устройств ввода-вывода, таких как мыши и клавиатуры.
В Unix-подобных системах наша энтропия накапливается в файле /dev/random
.
В Windows нет файла /dev/random .
Установка для параметра -Djava.security.egd значения
file:/dev/random
или file:/dev/urandom
приводит к тому, что алгоритм по умолчанию (SHA1PRNG) запускается с использованием собственного Microsoft Crypto API. **
`` **
10. Что насчет виртуальных машин?
Иногда наше приложение может работать на виртуальной машине, которая практически не собирает энтропию в /dev/random
.
Виртуальные машины не имеют физической мыши или клавиатуры для генерации данных , поэтому энтропия в /dev/random
накапливается гораздо медленнее. Это может привести к тому, что наш вызов SecureRandom
по умолчанию будет заблокирован до тех пор, пока не будет достаточно энтропии для генерации непредсказуемого числа.
Есть шаги, которые мы можем предпринять, чтобы смягчить это. Например, при запуске виртуальной машины в RedHat Linux системный администратор может настроить генератор случайных чисел виртуального ввода-вывода, virtio-rng
. Это считывает энтропию с физической машины, на которой она размещена.
11. Советы по устранению неполадок
Если наше приложение зависает, когда оно или его зависимости генерируют числа SecureRandom
, рассмотрите java.security.egd
— в частности, когда мы работаем в Linux и если мы работаем на версиях до Java 8.
Наши приложения Spring Boot часто используют встроенный Tomcat . Это использует SecureRandom
для генерации сеансовых ключей. Когда мы видим, что операция Tomcat «Создание экземпляра SecureRandom» занимает 5 секунд или более, мы должны попробовать разные значения для java.security.egd .
12. Заключение
В этом руководстве мы узнали, что такое свойство JVM java.security.egd
, как его использовать и какой эффект оно имеет. Мы также обнаружили, что его эффекты могут различаться в зависимости от платформы, на которой мы работаем, и используемой версии Java.
В заключение, мы можем прочитать больше о SecureRandom
и о том, как он работает, в разделе SecureRandom Справочного руководства JCA и Спецификации API SecureRandom, а также узнать о некоторых мифах о urandom .
Как обычно, код можно найти на GitHub .