How to Set TLS Version in Apache HttpClient – 如何在Apache HttpClient中设置TLS版本

最后修改: 2020年 9月 24日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

Apache HttpClient is a low-level, lightweight client-side HTTP library for communicating with HTTP servers. In this tutorial, we’ll learn how to configure the supported Transport Layer Security (TLS) version(s) when using HttpClient. We’ll begin with an overview of how TLS version negotiation works between a client and a server. Afterward, we’ll look at three different ways of configuring the supported TLS versions when using HttpClient.

Apache HttpClient是一个低级别的、轻量级的客户端HTTP库,用于与HTTP服务器进行通信。在本教程中,我们将学习如何在使用HttpClient时配置支持的传输层安全(TLS)版本。我们将首先概述一下客户端和服务器之间的TLS版本协商是如何进行的。之后,我们将看看在使用HttpClient时配置支持的TLS版本的三种不同方法

2. TLS Version Negotiation

2.TLS版本协商

TLS is an internet protocol that provides secure, trusted communication between two parties. It encapsulates application layer protocols like HTTP. The TLS protocol has been revised several times since it was first published in 1999. Therefore, it’s important for the client and server to first agree on which version of TLS they will use when establishing a new connection. The TLS version is agreed on after the client and server exchange hello messages:

TLS是一个互联网协议,在双方之间提供安全、可信的通信。它封装了HTTP等应用层协议。自1999年首次发布以来,TLS协议已被修订过多次。因此,在建立一个新的连接时,客户和服务器首先要同意他们将使用哪个版本的TLS。TLS版本是在客户和服务器交换hello消息后商定的。

  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.

It’s important to correctly configure the supported TLS versions of a web client because of the risk of a downgrade attack. Note that in order to use the latest version of TLS (TLS 1.3), we must be using Java 11 or later.

由于存在降级攻击的风险,正确配置网络客户端支持的TLS版本非常重要。注意,为了使用最新版本的TLS(TLS 1.3),我们必须使用Java 11或更高版本。

3. Setting the TLS Version Statically

3.静态设置TLS版本

3.1. SSLConnectionSocketFactory

3.1.SSLConnectionSocketFactory

Let’s use the HttpClientBuilder exposed by the HttpClients#custom builder method in order to customize our HTTPClient configuration. This builder pattern allows us to pass in our own SSLConnectionSocketFactory, which will be instantiated with the desired set of supported TLS versions:

让我们使用HttpClientBuilderHttpClients#custom构建器方法暴露出来,以便定制我们的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();

The returned Httpclient object can now execute HTTP requests. By setting the supported protocols explicitly in the SSLConnectionSocketFactory constructor, the client will only support communication over TLS 1.2 or TLS 1.3. Note that in Apache HttpClient versions prior to 4.3, the class was called SSLSocketFactory.

返回的Httpclient对象现在可以执行HTTP请求。通过在SSLConnectionSocketFactory构造器中明确设置支持的协议,客户端将只支持通过TLS 1.2或TLS 1.3进行通信。注意,在4.3之前的Apache HttpClient版本中,该类被称为SSLSocketFactory

3.2. Java Runtime Argument

3.2.Java运行时参数

Alternatively, we can configure the supported TLS versions using Java’s https.protocols system property. This method prevents having to hard-code values into the application code. Instead, we’ll configure the HttpClient to use the system properties when setting up connections. The HttpClient API provides two ways to do this. The first is via HttpClients#createSystem:

另外,我们可以使用 Java 的 https.protocols 系统属性来配置支持的 TLS 版本。这种方法可以避免在应用程序代码中硬编码数值。相反,我们将配置HttpClient以在设置连接时使用系统属性。HttpClient API提供了两种方法来做到这一点。第一种是通过HttpClients#createSystem

CloseableHttpClient httpClient = HttpClients.createSystem();

If more client configuration is required, we can use the builder method instead:

如果需要更多的客户端配置,我们可以使用构建器方法代替。

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

Both methods tell HttpClient to use system properties during connection configuration. This allows us to set the required TLS versions with a command-line argument during application runtime. For example:

这两种方法都告诉HttpClient在连接配置期间使用系统属性。这允许我们在应用程序运行期间用一个命令行参数设置所需的TLS版本。比如说。

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

4. Setting the TLS Version Dynamically

4.动态设置TLS版本

It’s also possible to set the TLS version based on connection details such as hostname and port. We’ll extend the SSLConnectionSocketFactory and override the prepareSocket method. The client calls the prepareSocket method before it initiates a new connection. This will let us decide which TLS protocols to use on a per-connection basis. It’s also possible to enable support for older TLS versions, but only if the remote host has a specific subdomain:

也可以根据连接细节(如主机名和端口)来设置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();

In the example above, the prepareSocket method first gets the remote hostname that the SSLSocket will connect to. The hostname is then used to determine with TLS protocols to enable. Now, our HTTP Client will enforce TLS 1.3 on every request except if the destination hostname is of the form *.internal.example.com. With the ability to insert custom logic before the creation of a new SSLSocket, our application can now customize the TLS communication details.

在上面的例子中,prepareSocket方法首先获得了SSLSocket将连接的远程主机名。<现在,我们的 HTTP 客户端将对每个请求强制执行 TLS 1.3,除非目标主机名是 *.internal.example.com. 由于能够在创建新的 SSLSocket 之前插入自定义逻辑,我们的应用程序现在可以自定义 TLS 通信细节。

5. Conclusion

5.总结

In this article, we looked at three different ways of configuring the supported TLS versions when using the Apache HttpClient library. We’ve seen how the TLS versions can be set for all connections, or on a per-connection basis. The code samples used in this article are available over on GitHub.

在这篇文章中,我们看了在使用Apache HttpClient库时配置支持的TLS版本的三种不同方式。我们已经看到了如何为所有的连接设置TLS版本,或者在每个连接的基础上设置。本文中所使用的代码样本可以在GitHub上找到