Может ли Java версии 1.8 генерировать то же значение SecureRandom, что и Java версии 1.6?
Я столкнулся с проблемой в системе аутентификации системы. Наши серверы используют версию 1.6, в то время как клиенты используют версию 1.8, в процессе аутентификации мы генерируем ключ путем "SHA1PRNG"
с SecureRandom
, а следующий код: т.е.
KeyGenerator keygen = KeyGenerator.getInstance("Blowfish");
SecureRandom foo = SecureRandom.getInstance("SHA1PRNG");
foo.setSeed("baa".getBytes());
keygen.init(foo);
Проблема в том, что мы обнаружили, что ключ, сгенерированный на клиентах, отличается от ключа на сервере. Мы устали распечатывать все шаги и обнаружили, что проблема вызвана SecureRandom
после foo.setSeed("baa".getBytes());
если мы позвоним foo.nextBytes()
, это даст разные значения.
Поэтому нам хотелось бы знать, есть ли способ заставить обе стороны генерировать одинаковое значение? (Учитывая, что версия Java как на клиенте, так и на сервере не может быть изменена.) Или не зависит от какой-либо платформы SecureRandom
метод в Java?
Справочная информация: SERVER и CLIENT работают в Unix. У меня есть рабочий стол под управлением Java 1.8, и я проверил следующее:
Desktop Java 1.8 может зашифровать и расшифровать ключ, сгенерированный в CLIENT (Java 1.8)
КЛИЕНТ (Java 1.8) не может зашифровать или расшифровать ключ, сгенерированный в SERVER (Java 1.6), и наоборот.
КЛИЕНТ установил Java 1.6 (только для тестирования), не может зашифровать или расшифровать ключ, сгенерированный в SERVER (Java 1.6). Мы предполагаем, что это потому, что
/dev/random
или же/dev/urandom
была перезаписана до версии Java 1.8. Поэтому даже версия Java одинакова, у них разное поведение.
2 ответа
Из документации для SecureRandom
:
Дополнительно,
SecureRandom
должен производить недетерминированный вывод. Поэтому любой семенной материал передаетсяSecureRandom
объект должен быть непредсказуемым, и всеSecureRandom
выходные последовательности должны быть криптографически стойкими, как описано в RFC 1750: Рекомендации по случайности для безопасности.
Таким образом, вы не только нарушаете требования SecureRandom
путем передачи предсказуемого семени, но явно требуется, чтобы вывод SecureRandom
непредсказуемо.
Для генерации предсказуемой последовательности случайности используйте Random
:
Если два случая
Random
создаются с одним и тем же начальным числом, и для каждого выполняется одинаковая последовательность вызовов методов, они будут генерировать и возвращать идентичные последовательности чисел.
Но учтите, что: если вы используете каждый раз одно и то же начальное число, то число всегда будет одним и тем же, поэтому вы должны использовать одно и то же начальное начальное число, которое каким-либо образом используется совместно между клиентом и сервером. Это начальное начальное число необходимо сбрасывать при каждом перезапуске серверного приложения.
Пример Random
должен быть разделен между вызовами к подпрограмме, иначе каждый раз будет генерироваться один и тот же номер:
public static void main(final String[] args) {
IntStream.range(1, 10)
.map(i -> new Random(42).nextInt())
.forEach(System.out::println);
}
Выход:
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
Вообще говоря, то, что вы пытаетесь сделать, - это Bad Idea TM. Вам было бы гораздо лучше использовать асимметричную схему шифрования, чем пытаться изобретать колесо самостоятельно.
Не изобретайте свои собственные ключевые функции отклонения. Используйте функции, придуманные для этого.
Вы не пишете то, что используете в качестве ввода для отклонения ключа, но если это пароль, вы можете использовать спецификацию ключа PBE (шифрование на основе пароля) - что-то вроде этого:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "Blowfish");