Как решить медленную Java "SecureRandom"?

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

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

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

18 ответов


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

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

SecureRandom.getInstance("SHA1PRNG");

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

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

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


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

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

однако это не работает с Java 5 и более поздними версиями (Ошибка Java 6202721). Предлагаемая работа-это использовать:

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

(обратите внимание на extra /./)


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

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

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

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


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


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

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

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

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

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

чтобы получить больше энтропии в /dev/random попробуйте haveged демон. Это демон, который непрерывно собирает энтропию 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 снижение безопасности кошельков Bitcoin.


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

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

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


используйте secure random в качестве источника инициализации для рекуррентного алгоритма; вы можете использовать тогда Mersenne twister для массовой работы вместо того, чтобы в UncommonMath, который был вокруг некоторое время и оказался лучше, чем другие prng

http://en.wikipedia.org/wiki/Mersenne_twister

убедитесь, что время от времени обновляется безопасный случайный, используемый для инициализации, например, вы можете иметь один безопасный случайный сгенерированный на клиента, используя один mersenne twister псевдослучайный генератор на одного клиента, получая достаточно высокую степень рандомизации


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

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

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

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

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


мой опыт был только с медленной инициализацией PRNG, а не с генерацией случайных данных после этого. Попробуйте более энергичную стратегию инициализации. Поскольку они дороги для создания, рассматривайте его как синглтон и повторно используйте тот же экземпляр. Если для одного экземпляра слишком много конфликтов потоков, объедините их или сделайте локальными потоками.

Не компрометируйте генерацию случайных чисел. Слабость там ставит под угрозу всю вашу безопасность.

Я не вижу много генераторов на основе атомного распада, но для них есть несколько планов, если вам действительно нужно много случайных данных. Один сайт, на котором всегда есть на что посмотреть, включая HotBits, это Джон Уокер.


Я столкнулся же вопрос. После некоторого поиска в Гугле с правильными условиями поиска я наткнулся на эту хорошую статью на DigitalOcean ИКТ.

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

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

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

как установить haveged

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

Я написал это здесь


похоже, вы должны быть яснее о своих требованиях к RNG. Самым сильным криптографическим требованием RNG (как я понимаю) было бы то, что даже если вы знаете алгоритм, используемый для их генерации, и знаете все ранее сгенерированные случайные числа, вы не можете получить никакой полезной информации о любом из случайных чисел, сгенерированных в будущем, не тратя непрактичное количество вычислительной мощности.

Если вам не нужна эта полная гарантия случайности, то есть, наверное, соответствующие эффективности. Я склонен согласиться с ответ Дэна Дайера об AESCounterRNG от Uncommons-Maths, или Fortuna (один из его авторов-Брюс Шнайер, эксперт по криптографии). Я никогда не использовал ни то, ни другое, но идеи кажутся авторитетными на первый взгляд.

Я думаю что если вы можете генерировать начальное случайное семя периодически (например, один раз в день или час или что-то еще), вы можете использовать быстрый потоковый шифр для генерируйте случайные числа из последовательных кусков потока (если потоковый шифр использует XOR, то просто передайте поток нулей или захватите биты XOR напрямую). ECRYPT это eStream есть много хорошей информации, включая контрольные показатели. Это не будет поддерживать энтропию между точками во времени, которые вы ее пополняете, поэтому, если кто-то знает одно из случайных чисел и алгоритм, который вы использовали, технически возможно, с большой вычислительной мощностью, сломать потоковый шифр и угадайте его внутреннее состояние, чтобы иметь возможность предсказывать будущие случайные числа. Но вам придется решить, достаточен ли этот риск и его последствия для оправдания затрат на поддержание энтропии.

Edit: вот некоторые криптографические заметки курса по RNG Я нашел в сети, которые выглядят очень актуальна к этой теме.


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

rngd -r /dev/urandom

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

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


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

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

обновление: хорошо, я нашел это отличное объяснение.

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

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


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


Что-то еще, чтобы посмотреть на это свойство securerandom.источник в файле lib / безопасность / java.безопасность

использование /dev/urandom вместо /dev/random может иметь преимущество в производительности. Помните, что если качество случайных чисел важно, не делайте компромисс, который нарушает безопасность.


Если ваше оборудование поддерживает его, попробуйте использовать утилиту Java RdRand, доступную на: http://code.google.com/p/lizalab-rdrand-util/

Он основан на инструкции Intel RDRAND и примерно в 10 раз быстрее, чем SecureRandom, и никаких проблем с пропускной способностью для реализации большого объема.

полное раскрытие, я автор программы.


вы можете попробовать Apache commons Math project, который имеет некоторые реализации хорошо известных алгоритмов:

https://commons.apache.org/proper/commons-math/userguide/random.html

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

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


постановка задачи

библиотека msprandom демонстрирует технику генерации случайных чисел для криптографических целей без аппаратных генераторов. Шифрование и подписание требует случайных чисел с хорошим качеством. Генерация случайных чисел (или последовательностей случайных байтов) без аппаратных генераторов не является тривиальной задачей. Особенно эта проблема актуальна для небольших устройств, где имеются источники случайных данных отсутствуют или ограничены. Этот решение состоит в том, чтобы иметь истинное случайное семя, сохраненное в защищенном файле (хранилище) и шифре, который может создавать зашифрованные псевдослучайные генерируемые последовательности (PRNG) на основе случайного семени с хорошими случайными характеристиками.

многие криптографические библиотеки (например, BouncyCastle) используют класс SecureRandom для шифрования и подписи для получения случайных чисел. SecureRandom зависит от реализации ОС. Другими словами, реализация random engine находится вне вашего приложения, которое вы не можете контролировать. Чтобы избежать используя плохие случайные числа, вы должны посеять генератор SecureRandom с хорошими случайными данными каждый раз, когда вы вызываете криптографические функции, которые требуют случайных данных. Или вы можете расширить класс SecureRandom с вашей реализацией, которая производит случайные числа, качество которых вы можете контролировать.

идея

мы должны использовать истинно случайные данные, хранящиеся в защищенном хранилище данных.

некоторые шаги, как msprandom внутри вашего приложения:

  1. создайте на своем компьютере или ноутбуке истинное случайное семя и поместите его в хранилище с помощью этой библиотеки.
  2. поместите хранилище (файл) со случайным семенем на устройство, компьютер или сервер, где вам нужно зашифровать и подписать данные.
  3. загрузите хранилище один раз в начале программы, когда вам нужно зашифровать или подписать данные.
  4. вызовите функцию gen-rand из библиотеки msprandom, чтобы получить случайные байты столько раз, сколько вы необходимость.

хранилище со случайным семенем зашифровано и защищено с помощью HMAC. Случайное семя в хранилище обновляется каждый раз, когда вы загружаете хранилище непредсказуемым способом, поэтому HMAC тоже меняется. Изменение хранилища производится намеренно против ситуации, если злоумышленник может обогатить некоторую копию вашего хранилища в прошлом.

истинный генератор случайных данных

для генерации истинного случайного семени человеческий вход используется в msprandom. Здесь алгоритм сбора случайных данных:

  1. мы запускаем отдельный поток, где атомарный счетчик увеличивает каждый тик от 0..255 с очень высокой скоростью.
  2. подождите, пока unbuffered нажмите клавишу человеком и получить код сканирования нажатой кнопки.
  3. возьмите текущее значение наносекунд от начала эпохи и возьмите mod 256, чтобы преобразовать его значение в случайный байт.
  4. значения Xor между собой: scan-code-byte ^ current-counter-value ^ наносекунды до произведите случайный байт.
  5. добавить произвольный байт в выходной вектор. Мы предполагаем, что только 3 бита имеют истинную случайность в этом случайном байте. Итак, чтобы получить истинные случайные 32 байта, нам нужно нажать кнопку ~ 32*3 от пользовательского ввода.
  6. повторите шаги 2-5, пока мы не получим требуемого количества случайных байт. Если мы собрали необходимое количество случайных данных, то сделайте последний шаг - > вектор хэш-вывода с криптографически сильной хэш-функцией, чтобы гарантировать, что вероятность 1 и 0 бит в выходном векторе будет быть 0.5. Обратите внимание, что хэш-функция используется здесь только для смешивания случайных битов и не влияет на качество случайных данных. Итак, хэш (случайные данные) = случайные данные. Используя этот алгоритм, msprandom собирает истинные 512 случайных битов в качестве семени,которое будет сохранено в хранилище.

почему 512 случайных битов достаточно?

ну, каждому PRNG нужно истинное случайное семя. Если злоумышленник знает семя, то он может предсказать генерацию ключа и так далее. 256 бит начального случайного семя достаточно далеко, чтобы хранить секреты военного класса. Я сделал 512, чтобы убедиться, что никто не может грубой силы или угадать начальное случайное семя. Таким образом, вы можете свободно использовать msprandom для посева генераторов PRNG или SecureRandom.