Java Spring Boot 2.11 не будет использовать TLS 1.3 для исходящего подключения к API

У меня есть API, с которым я пытаюсь выполнить интеграцию, и этот API поддерживает только TLSv1.3. Я попробовал несколько различных стратегий подключения, но без каких-либо реальных изменений и надеялся, что кто-нибудь укажет мне правильное направление. Вот пример кода, иллюстрирующий мою проблему:

import javax.net.ssl.SSLContext;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) {
        String apiUrl = "https://api-url.com";

        try {
            // Create an SSLContext with TLS 1.3
            SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
            sslContext.init(null, null, null);

            // Create an HttpClient with the SSLContext
            HttpClient httpClient = HttpClient.newBuilder()
                    .version(HttpClient.Version.HTTP_2)
                    .sslContext(sslContext)
                    .build();

            // Create an HttpRequest
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(apiUrl))
                    .GET()
                    .header("Accept", "application/json")
                    .build();

            // Send the request and retrieve the response
            HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

            // Process the response
            if (response.statusCode() == 200) {
                String responseBody = response.body();
                System.out.println("Response Body: " + responseBody);
            } else {
                System.out.println("Request failed. Status code: " + response.statusCode());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Этот код работает, когда это автономное приложение успешно подключается к API. Когда я беру этот же код и помещаю его в службу в своем приложении весенней загрузки, происходит сбой из-за сбоя подтверждения SSL:

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:130)

at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)

at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:365)

at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:287)

at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:204)

at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)

at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1507)

Я вижу ClientHello, когда у меня включена отладка, и он явно отправляет TLSv1.2:

javax.net.ssl|DEBUG|F6|XNIO-1 task-1|2024-04-03 23:07:47.320 EDT|ClientHello.java:640|Produced ClientHello handshake message (

"ClientHello": {

"client version" : "TLSv1.2",

"random" : "FDA9B3D29A6C9F53E773999AFB00B22C4CB2D6103266BDE2B783DF2CBC8A82BA",

"session id" : "A60300E77225F0C6A66FF1D40A90ED38CBC5559DF77BCF7EC5E7A65999109417",

"cipher suites" : "[TLS_AES_256_GCM_SHA384(0x1302), TLS_AES_128_GCM_SHA256(0x1301), TLS_CHACHA20_POLY1305_SHA256(0x1303)]",

"compression methods" : "00",

Используя OpenSSL для подключения с моего терминала, я вижу, что требуется TLSv1.3.

SSL handshake has read 2632 bytes and written 418 bytes

Verification: OK

---

New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384

Server public key is 256 bit

This TLS version forbids renegotiation.

Compression: NONE

Expansion: NONE

No ALPN negotiated

Early data was not sent

Verify return code: 0 (ok)

---

С помощью System.GetProperty я проверил, что jdk.tls.client.protocols и https.protocols не установлены, поэтому используются значения по умолчанию. Я также попробовал установить для них обоих значение TLSv1.3, но при выполнении запроса он по-прежнему использует версию 1.2.

Это приложение также использует Java 11, который, как я подтвердил, поддерживает TLSv1.3 по умолчанию. Я также запускал его с использованием Java 22 (который, к моему удивлению, действительно работал), но у меня и там та же проблема.

Я немного заржавел в Java и не СУПЕР знаком с пружинной загрузкой в ​​целом, но есть ли что-то, чего мне здесь не хватает, чтобы заставить эту штуку использовать TLSv1.3 при создании исходящего соединения?

AFAIK, версия весенней загрузки здесь не имеет значения, важнее, какую версию JDK вы используете. Кроме того: будьте очень осторожны при принятии решения о том, какая версия TLS используется на основе первоначального рукопожатия, потому что это сбивает с толку: TLSv 1.3 требует, чтобы в некоторых местах утверждалось, что это 1.2, а в других - сообщали о способности говорить 1.3, просто потому, что в одном месте это говорит "1,2" не значит, что это 1,2: Объяснение смотрите здесь.

Joachim Sauer 04.04.2024 13:48

Это имеет смысл, и я вижу, что это происходит в автономном приложении. Есть ли более глубокий способ отладки этого? В автономном приложении с включенной отладкой я сразу вижу ServerHello и остальную часть переговоров, в которых согласовывается TLSv1.3. Однако внутри приложения весенней загрузки после ClientHello оно просто получает предупреждение о сбое рукопожатия и никакой дополнительной информации не требуется.

Brad Gardner 04.04.2024 14:03
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
113
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Спасибо Штеффену Ульриху в комментариях к ServerFault, который указал, что SNI может вызвать эту проблему. Я обнаружил, что в другом месте этого Java-приложения оно отключено:

System.setProperty("jsse.enableSNIExtension", "false");

исправление этого, установив для него значение true, устраняет мою непосредственную проблему.

Другие вопросы по теме