«1. Введение

Apache HttpClient — это низкоуровневая облегченная клиентская HTTP-библиотека для связи с HTTP-серверами. В этом руководстве мы узнаем, как настроить поддерживаемые версии (версии) безопасности транспортного уровня (TLS) при использовании HttpClient. Мы начнем с обзора того, как работает согласование версии TLS между клиентом и сервером. После этого мы рассмотрим три различных способа настройки поддерживаемых версий TLS при использовании HttpClient.

2. Согласование версии TLS

TLS — это интернет-протокол, обеспечивающий безопасную и надежную связь между двумя сторонами. Он инкапсулирует протоколы прикладного уровня, такие как HTTP. Протокол TLS несколько раз пересматривался с момента его первой публикации в 1999 году. Поэтому важно, чтобы клиент и сервер сначала договорились о том, какую версию TLS они будут использовать при установлении нового соединения. Версия TLS согласовывается после обмена приветственными сообщениями между клиентом и сервером:

  1. The client sends a list of supported TLS versions.
  2. The server chooses one and includes the selected version in the response.
  3. The client and server continue the connection setup using the selected version.

Важно правильно настроить поддерживаемые версии TLS веб-клиента из-за риска атаки с понижением версии. Обратите внимание: чтобы использовать последнюю версию TLS (TLS 1.3), мы должны использовать Java 11 или более позднюю версию.

3. Статическая установка версии TLS

3.1. SSLConnectionSocketFactory

Давайте воспользуемся HttpClientBuilder, предоставляемым методом HttpClients#custom Builder, чтобы настроить нашу конфигурацию HTTPClient. Этот шаблон построителя позволяет нам передать нашу собственную фабрику SSLConnectionSocketFactory, которая будет создана с желаемым набором поддерживаемых версий TLS:

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
  SSLContexts.createDefault(),
  new String[] { "TLSv1.2", "TLSv1.3" },
  null,
  SSLConnectionSocketFactory.getDefaultHostnameVerifier());

CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

Возвращенный объект Httpclient теперь может выполнять HTTP-запросы. Задав поддерживаемые протоколы явно в конструкторе SSLConnectionSocketFactory, клиент будет поддерживать связь только через TLS 1.2 или TLS 1.3. Обратите внимание, что в версиях Apache HttpClient до 4.3 класс назывался SSLSocketFactory.

3.2. Аргумент Java Runtime

Кроме того, мы можем настроить поддерживаемые версии TLS, используя системное свойство Java https.protocols. Этот метод избавляет от необходимости жестко записывать значения в код приложения. Вместо этого мы настроим HttpClient для использования системных свойств при настройке соединений. API HttpClient предоставляет два способа сделать это. Первый — через HttpClients#createSystem:

CloseableHttpClient httpClient = HttpClients.createSystem();

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

CloseableHttpClient httpClient = HttpClients.custom().useSystemProperties().build();

Оба метода сообщают HttpClient использовать системные свойства во время настройки подключения. Это позволяет нам устанавливать требуемые версии TLS с помощью аргумента командной строки во время выполнения приложения. Например:

$ java -Dhttps.protocols=TLSv1.1,TLSv1.2,TLSv1.3 -jar webClient.jar

4. Динамическая установка версии TLS

Также можно установить версию TLS на основе сведений о соединении, таких как имя хоста и порт. Мы расширим SSLConnectionSocketFactory и переопределим метод prepareSocket. Клиент вызывает метод prepareSocket перед тем, как инициировать новое соединение. Это позволит нам решить, какие протоколы TLS использовать для каждого соединения. Также можно включить поддержку более старых версий TLS, но только если удаленный хост имеет определенный субдомен:

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(SSLContexts.createDefault()){

    @Override
    protected void prepareSocket(SSLSocket socket) {

        String hostname = socket.getInetAddress().getHostName();
        if (hostname.endsWith("internal.system.com")){
            socket.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" });
        }
        else {
            socket.setEnabledProtocols(new String[] {"TLSv1.3"});
        }
    }
};<br />
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

В приведенном выше примере метод prepareSocket сначала получает имя удаленного хоста, к которому будет подключаться SSLSocket. Затем имя хоста используется для определения включения протоколов TLS. Теперь наш HTTP-клиент будет применять TLS 1.3 при каждом запросе, за исключением случаев, когда имя хоста назначения имеет вид *.internal.example.com. Благодаря возможности вставлять пользовательскую логику перед созданием нового SSLSocket наше приложение теперь может настраивать детали связи TLS.

5. Заключение

В этой статье мы рассмотрели три разных способа настройки поддерживаемых версий TLS при использовании библиотеки Apache HttpClient. Мы видели, как можно установить версии TLS для всех соединений или для каждого соединения отдельно. Примеры кода, использованные в этой статье, доступны на GitHub.