Может ли 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, и я проверил следующее:

  1. Desktop Java 1.8 может зашифровать и расшифровать ключ, сгенерированный в CLIENT (Java 1.8)

  2. КЛИЕНТ (Java 1.8) не может зашифровать или расшифровать ключ, сгенерированный в SERVER (Java 1.6), и наоборот.

  3. КЛИЕНТ установил 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");
Другие вопросы по тегам