Как бороться с медленным генератором SecureRandom?

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

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

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

Ответ 1

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

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

SecureRandom.getInstance("SHA1PRNG");

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

Многие дистрибутивы 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

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


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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

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

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

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

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

Ответ 8

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

rngd -r /dev/urandom

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

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

Ответ 9

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

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

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

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

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

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

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

Ответ 10

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

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

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

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

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

Ответ 11

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

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

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

Ответ 12

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

Ответ 13

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

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

Я не вижу много генераторов COTS Atom-decay &ndash, но для них существует несколько планов, если вам действительно нужно много случайных данных. На одном сайте, на котором всегда есть интересные вещи, включая HotBits, есть John Walker Fourmilab.

Ответ 14

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

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

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

Ответ 15

Если ваше оборудование поддерживает это, попробуйте использовать Java RdRand Utility, автором которой я являюсь.

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


Обратите внимание, что эта реализация работает только на тех CPU, которые предоставляют инструкцию (т. rdrand Когда установлен rdrand процессора rdrand). Вам необходимо явно создать его экземпляр с помощью конструктора RdRandRandom(); конкретный Provider не был реализован.

Ответ 16

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

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

Ответ 17

Описание проблемы

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

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

Идея

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

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

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

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

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

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

  • Мы запускаем отдельный поток, где атомный счетчик увеличивает каждый tic от 0..255 с очень высокой скоростью.
  • Подождите, пока не будет нажата клавиша "небуферизованная клавиша", и получите код сканирования нажатой кнопки.
  • Возьмите текущее значение наносекунд с начала Epoch и возьмите mod 256, чтобы преобразовать его значение в случайный байт.
  • Значения Xor между собой: scan-code-byte ^ current-counter-value ^ nanoseconds для создания случайного байта.
  • Добавить случайный байт в выходной вектор. Мы полагаем, что только 3 бита имеют истинную случайность в этом случайном байте. Итак, чтобы получить истинные случайные 32 байта, нам нужно нажать кнопку ~ 32 * 3 с пользовательского ввода.
  • Повторяйте шаги 2-5, пока не получите требуемое количество случайных байтов. Если мы собрали требуемое количество случайных данных, то сделайте окончательный шаг → хэш-выходной вектор с криптографически сильной хэш-функцией, чтобы гарантировать, что вероятность 1 и 0 бит в выходном векторе будет равна 0,5. Обратите внимание, что эта хэш-функция используется здесь только для смешивания случайных бит и не влияет на качество случайных данных. Так хэш (случайные данные) = случайные данные. Используя этот алгоритм, msprandom собирает истинные 512 случайных бит в качестве семени, которые будут сохранены хранилищем.

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

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

Ответ 18

Согласно документации, различные алгоритмы, используемые SecureRandom, в порядке предпочтения:

  • В большинстве * систем NIX
    1. NativePRNG
    2. SHA1PRNG
    3. NativePRNGBlocking
    4. NativePRNGNonBlocking
  • В системах Windows
    1. SHA1PRNG
    2. Окна-ПСЧ

Поскольку вы спрашивали о Linux, я игнорирую реализацию Windows, а также SunPKCS11, который действительно доступен только в Solaris, если вы не установили его самостоятельно - и тогда вы бы не спрашивали об этом.

Согласно той же документации, эти алгоритмы используют

SHA1PRNG
Начальное заполнение в настоящее время выполняется с помощью комбинации системных атрибутов и устройства сбора энтропии java.security.

NativePRNG
nextBytes() использует /dev/urandom
generateSeed() использует /dev/random

NativePRNGBlocking
nextBytes() и generateSeed() используют /dev/random

NativePRNGNonBlocking
nextBytes() и generateSeed() используют /dev/urandom

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

Поскольку /dev/random блокируется (блокируется до тех пор, пока не будет достаточно энтропии в пуле энтропии), это может снизить производительность.

Одним из решений этого является использование чего-то вроде hasged для генерации достаточного количества энтропии, другое решение использует вместо этого /dev/urandom. Хотя вы можете установить это для всего jvm, лучшим решением будет сделать это для этого конкретного экземпляра SecureRandom, используя SecureRandom random = SecureRandom.getInstance("NativePRNGNonBlocking"). Обратите внимание, что этот метод может генерировать исключение NoSuchAlgorithmException, если NativePRNGNonBlocking, поэтому будьте готовы к отступлению от значения по умолчанию.

SecureRandom random;
try {
    random = SecureRandom.getInstance("NativePRNGNonBlocking");
} catch (NoSuchAlgorithmException nsae) {
    random = new SecureRandom();
}

Также обратите внимание, что в других * nix-системах /dev/urandom может вести себя по-разному.


Достаточно ли случайен /dev/urandom?

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

Пользователи из стека информационной безопасности согласны с этим. По сути, если вам нужно спросить, /dev/urandom подходит для ваших целей.

Ответ 19

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

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

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

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