Как выборочно использовать режим «только одобренный» с BouncyCastle JSSE provider FIPS provider?

#java #bouncycastle #java-security #fips

#java #bouncycastle #java-безопасность #fips

Вопрос:

В небольшом фрагменте примера кода, работающего на Java 8, я пытаюсь следовать совету из bc-fips-1.0.2.jar руководство пользователя, Le&ion of the Bouncy Castle Inc. BC-FJA 1.0.2 (Bouncy Castle FIPS Java API) Дата выхода руководства пользователя: 14.09.19:

Примечание: поддержка режима FIPS, похоже, начала исчезать с Java 1.9, было подтверждено, что это нулевая операция начиная с Java 11. Для Java 11 или более поздней версии мы рекомендуем использовать BCJSSE provider, если вам нужна поддержка FIPS.

(сноска на стр. 11). И я делаю это в «смешанном» контексте, поэтому большинство потоков выполняются в режиме по умолчанию, а некоторые — в режиме «только одобренный FIPS».

Однако, похоже, это не работает: похоже, что «одобренный» поток пытается повторно использовать и экземпляр создается «неутвержденным» потоком.

В частности, при программном включении bc-fips-1.0.2.jar BCFIPS и bctls-fips-1.0.10.jar BCJSSE в стандартном неизмененном OpenJDK 8 (в моем случае Corretto 8u232) и программном удалении встроенного поставщика SunJSSE, затем выполнении HTTPS-соединения в новом потоке в неутвержденном режиме, затем в другом новом потоке в одобренном режиме приводит к исключению:

 Caused by: or&.bouncycastle.crypto.fips.FipsUnapprovedOperationError: Attempt to use unapproved implementation in approved thread: SHA-512
    at or&.bouncycastle.crypto.internal.io.Utils.approvedModeCheck(Unknown Source)
    at or&.bouncycastle.crypto.internal.io.Di&estOutputStream.write(Unknown Source)
    at or&.bouncycastle.crypto.UpdateOutputStream.update(Unknown Source)
    at or&.bouncycastle.jcajce.provider.BaseMessa&eDi&est.en&ineUpdate(Unknown Source)
    at java.security.Messa&eDi&est.update(Messa&eDi&est.java:335)
    at or&.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider$NonceEntropySource$NonceEntropySourceSpi.runDi&est(Unknown Source)
    at or&.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider$NonceEntropySource$NonceEntropySourceSpi.en&ineNextBytes(Unknown Source)
    at java.security.SecureRandom.nextBytes(SecureRandom.java:468)
    at or&.bouncycastle.tls.crypto.impl.jcajce.JcaNonceGenerator.<init&&t;(Unknown Source)
    at or&.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto.createNonceGenerator(Unknown Source)
    at or&.bouncycastle.tls.AbstractTlsContext.createNonceGenerator(Unknown Source)
    at or&.bouncycastle.tls.AbstractTlsContext.<init&&t;(Unknown Source)
    at or&.bouncycastle.tls.TlsClientContextImpl.<init&&t;(Unknown Source)
[etc]
  

Сценарий полного воспроизведения моих попыток находится на https://&ist.&ithub.com/marnix/834610d0fb92e53ce507edfce96bacb9 подробности см. в javadoc класса.

Итак, мой вопрос: как мне заставить работать HTTPS-соединения, где некоторые находятся в режиме по умолчанию, а другие — в режиме «только одобренный FIPS», используя BCJSSE вместо SunJSSE?

Или это не будет работать на Java 8, и мне нужно ждать Java 9 , чтобы это начало работать? (Почему?)

(Моя конечная цель — не для HTTPS-соединений, а для других целей, где большинство будет выполняться в режиме по умолчанию, а некоторые потоки будут выполняться в режиме «только одобренный».)

Ответ №1:

Использование отдельных экземпляров SSLContext — правильный подход, но использование SSLContext.setDefault здесь очень неправильно. Отчасти потому, что, как только вы удалите объединения потоков, возникнут условия гонки без установщиков. Однако, что еще более важно, HttpsURLConnection устанавливает SSLSocketFactory по умолчанию только один раз и вызовет SSLContext.&etDefault не более одного раза, чтобы установить его, поэтому вызов SSLContext.setDefault после этого не окажет никакого влияния на HttpsURLConnection, и вы получите общий доступ к исходным ошибкам SSLContext и FIPS.

Удалить:

 SSLContext.setDefault(context);
  

и заменить:

 url.openConnection()
  

с:

 HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(context.&etSocketFactory());
  

Комментарии:

1. Большое спасибо за ваш ответ! Это объяснение понятно и имеет большой смысл. Я протестировал его, и теперь он работает как шарм.