Как решить медленную Java `SecureRandom`? - PullRequest
148 голосов
/ 26 сентября 2008

Если вы хотите криптографически сильное случайное число в Java, вы используете SecureRandom. К сожалению, SecureRandom может быть очень медленным. Если он использует /dev/random в Linux, он может заблокировать ожидание достаточной энтропии для наращивания. Как избежать штрафа за производительность?

Кто-нибудь использовал Uncommon Maths в качестве решения этой проблемы?

Кто-нибудь может подтвердить, что эта проблема с производительностью была решена в JDK 6?

Ответы [ 17 ]

168 голосов
/ 24 февраля 2010

Вы должны быть в состоянии выбрать более быстрый, но немного менее безопасный / dev / urandom в Linux, используя:

-Djava.security.egd=file:/dev/urandom

Однако это не работает с Java 5 и более поздними версиями ( Java Bug 6202721 ). Предложенный обходной путь должен использовать:

-Djava.security.egd=file:/dev/./urandom

(обратите внимание на дополнительные /./)

69 голосов
/ 26 сентября 2008

Если вам нужны настоящие случайные данные, то, к сожалению, вам придется их ждать. Это включает семя для SecureRandom PRNG. Необычные математики не могут собирать настоящие случайные данные быстрее, чем SecureRandom, хотя они могут подключаться к Интернету для загрузки начальных данных с определенного веб-сайта. Я думаю, что это вряд ли будет быстрее, чем /dev/random, где это доступно.

Если вы хотите PRNG, сделайте что-то вроде этого:

SecureRandom.getInstance("SHA1PRNG");

Какие строки поддерживаются, зависит от SecureRandom поставщика SPI, но вы можете перечислить их, используя Security.getProviders() и Provider.getService().

Солнце любит SHA1PRNG, поэтому оно широко доступно. Это не так быстро, как PRNG, но PRNG будут просто вычислять цифры, а не блокировать физическое измерение энтропии.

Исключением является то, что если вы не позвоните setSeed() до получения данных, то PRNG будет запущен сам при первом вызове next() или nextBytes(). Обычно это делается с использованием довольно небольшого количества истинных случайных данных из системы. Этот вызов может блокировать, но сделает ваш источник случайных чисел намного более безопасным, чем любой вариант «хеширования текущего времени вместе с PID, добавьте 27 и надейтесь на лучшее». Если все, что вам нужно, это случайные числа для игры, или если вы хотите, чтобы поток повторялся в будущем с использованием того же начального числа для целей тестирования, небезопасное начальное число все еще полезно.

33 голосов
/ 26 сентября 2008

В Linux реализация по умолчанию для SecureRandom - NativePRNG (исходный код здесь ), что имеет тенденцию быть очень медленным. В Windows по умолчанию используется значение SHA1PRNG, которое, как указывали другие, также можно использовать в Linux, если вы укажете это явно.

NativePRNG отличается от SHA1PRNG и AESCounterRNG Uncommons Maths тем, что непрерывно получает энтропию от операционной системы (путем чтения из /dev/urandom). Другие PRNG не приобретают никакой дополнительной энтропии после посева.

AESCounterRNG примерно в 10 раз быстрее, чем SHA1PRNG, что само по себе IIRC в два или три раза быстрее, чем NativePRNG.

Если вам нужен более быстрый PRNG, который приобретает энтропию после инициализации, посмотрите, можете ли вы найти реализацию Java Fortuna . Базовый PRNG реализации Fortuna идентичен тому, который используется AESCounterRNG, но также существует сложная система объединения энтропии и автоматического повторного заполнения.

20 голосов
/ 03 ноября 2017

Многие дистрибутивы Linux (в основном на основе Debian) настраивают OpenJDK для использования /dev/random для энтропии.

/dev/random по определению медленный (и может даже блокировать).

Отсюда у вас есть два варианта разблокировки:

  1. Улучшение энтропии или
  2. Уменьшить требования к случайности.

Вариант 1, Улучшение энтропии

Чтобы получить больше энтропии в /dev/random, попробуйте демон hasged . Это демон, который непрерывно собирает энтропию HAVEGE и работает также в виртуализированной среде, поскольку ему не требуется никакого специального оборудования, только сам процессор и часы.

В Ubuntu / Debian:

apt-get install haveged
update-rc.d haveged defaults
service haveged start

На RHEL / CentOS:

yum install haveged
systemctl enable haveged
systemctl start haveged

Вариант 2. Снижение требований случайности

Если по какой-то причине вышеприведенное решение не помогает или вас не волнует криптографически сильная случайность, вы можете вместо этого переключиться на /dev/urandom, который гарантированно не блокирует.

Чтобы сделать это глобально, отредактируйте файл jre/lib/security/java.security в вашей установке Java по умолчанию, чтобы использовать /dev/urandom (из-за другой ошибки его нужно указать как /dev/./urandom).

Как это:

#securerandom.source=file:/dev/random
securerandom.source=file:/dev/./urandom

Тогда вам не нужно будет указывать это в командной строке.


Примечание: если вы занимаетесь криптографией, вам нужна хорошая энтропия. Показательный пример - проблема с Android PRNG снижает безопасность кошельков Биткойн.

16 голосов
/ 20 мая 2015

У меня была похожая проблема с вызовами на SecureRandom блокировку примерно на 25 секунд за раз на безголовом сервере Debian. Я установил демон haveged, чтобы обеспечить постоянную загрузку /dev/random, на серверах без монитора вам нужно что-то подобное для генерации необходимой энтропии. Мои звонки на SecureRandom теперь, возможно, занимают миллисекунды.

11 голосов
/ 26 сентября 2008

Если вы хотите действительно «криптографически сильную» случайность, то вам нужен сильный источник энтропии. /dev/random медленный, потому что он должен ждать, пока системные события соберут энтропию (чтение диска, сетевые пакеты, движение мыши, нажатия клавиш и т. Д.).

Более быстрое решение - аппаратный генератор случайных чисел. Возможно, у вас уже есть один встроенный в материнскую плату; Изучите документацию hw_random , чтобы узнать, как выяснить, есть ли она у вас и как ее использовать. В пакет rng-tools входит демон, который будет передавать энтропию, генерируемую оборудованием, в /dev/random.

Если HRNG недоступен в вашей системе, и вы готовы пожертвовать силой энтропии для повышения производительности, вам нужно создать хороший PRNG с данными из /dev/random и позволить PRNG выполнять большую часть работы. В SP800-90 имеется несколько утвержденных NIST PRNG, которые легко внедрить.

5 голосов
/ 28 августа 2012

Существует инструмент (по крайней мере, в Ubuntu), который подает искусственную случайность в вашу систему. Команда просто:

rngd -r /dev/urandom

и вам может понадобиться sudo на передней панели. Если у вас нет пакета rng-tools, вам необходимо установить его. Я попробовал это, и это определенно помогло мне!

Источник: Мэтт против мира

5 голосов
/ 17 ноября 2016

Я сталкивался с такой же проблемой . После некоторого поиска в Google с правильными поисковыми терминами я наткнулся на эту прекрасную статью о DigitalOcean .

есть потенциальное решение без ущерба для безопасности.

Я просто цитирую соответствующую часть статьи здесь.

На основе принципа HAVEGE и ранее на основе связанных с ним библиотека hasged позволяет генерировать случайность на основе вариаций время выполнения кода на процессоре. Поскольку это почти невозможно для один кусок кода, чтобы выполнить то же самое точное время, даже в та же среда на том же оборудовании, время запуска одного или несколько программ должны подходить для создания случайного источника. Реализована реализация случайного источника вашей системы (обычно / dev / random) используя различия в счетчике меток времени вашего процессора (TSC) после многократного выполнения цикла

Как установить hasged

Выполните действия, описанные в этой статье. https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged

Я разместил это здесь

4 голосов
/ 26 сентября 2008

Проблема, на которую вы ссылались по поводу /dev/random, связана не с алгоритмом SecureRandom, а с источником случайности, который он использует. Два ортогональны. Вы должны выяснить, какой из двух замедляет вас.

На странице "Необычные математики", на которую вы ссылаетесь, явно упоминается, что они не обращаются к источнику случайности.

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

Краткий поиск также показывает патчи Linux, которые заменяют реализацию по умолчанию на Fortuna. Я не знаю больше об этом, но вы можете расследовать.

Я должен также упомянуть, что, хотя очень опасно использовать плохо реализованный алгоритм SecureRandom и / или источник случайности, вы можете свернуть свой собственный поставщик JCE с пользовательской реализацией SecureRandomSpi. Вам нужно будет пройти через процесс с Sun, чтобы подписать вашего провайдера, но на самом деле это довольно просто; им просто нужно, чтобы вы отправили им по факсу форму, подтверждающую, что вам известны ограничения на экспорт криптобиблиотек из США.

4 голосов
/ 30 ноября 2016

Используя Java 8, я обнаружил, что в Linux вызов SecureRandom.getInstanceStrong() даст мне алгоритм NativePRNGBlocking. Это часто блокировало бы в течение многих секунд, чтобы генерировать несколько байтов соли.

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

Обновление : Хорошо, я нашел это превосходное объяснение .

В двух словах, чтобы избежать блокировки, используйте new SecureRandom(). При этом используется /dev/urandom, который не блокирует и в основном такой же безопасный, как /dev/random. Из поста: «Единственный раз, когда вы захотите вызвать / dev / random, это когда машина загружается впервые, а энтропия еще не накопилась».

SecureRandom.getInstanceStrong() дает вам самый сильный ГСЧ, но его можно использовать только в ситуациях, когда на вас не повлияет куча блокировок.

...