1. Overview
1.概述
In this article, we’ll see how to initialize and configure an OkHttpClient to trust self-signed certificates. For this purpose, we’ll set up a minimal HTTPS-enabled Spring Boot application secured by a self-signed certificate.
在这篇文章中,我们将看到如何初始化和配置OkHttpClient以信任自签名证书。为此,我们将建立一个最小的启用了HTTPS的Spring Boot应用程序,由一个自签名的证书担保。
Refer to our collection of articles on OkHttp for more specifics on the library.
请参考我们的关于OkHttp的文章集,以了解关于该库的更多具体信息。
2. The Fundamentals
2.基本原理
Before we dive into the code responsible for making this work, let’s hit the bottom line. The essence of SSL is that it establishes a secure connection between any two parties, commonly a client and a server. Also, it helps in safeguarding the privacy and integrity of data transferred over a network.
在我们深入了解负责使其发挥作用的代码之前,让我们先来打探一下底线。SSL的本质是它在任何两方之间建立了安全连接,通常是客户端和服务器。此外,它还有助于保护通过网络传输的数据的隐私和完整性。
The JSSE API, a security component of the Java SE, provides complete API support for the SSL/TLS protocol.
JSSE API是Java SE的一个安全组件,为SSL/TLS协议提供完整的API支持。
SSL certificates, a.k.a digital certificates, play a vital role in establishing a TLS handshake, facilitating encryption and trust between the communicating parties. Self-signed SSL certificates are the ones that aren’t issued by a well-known and trusted certificate authority (CA). They can be easily generated and signed by a developer to enable HTTPS for their software.
SSL证书,又称数字证书,在建立TLS握手过程中发挥了重要作用,促进了通信双方的加密和信任。自签的SSL证书不是由知名和受信任的证书机构(CA)颁发的。他们可以很容易地生成和签署,由开发人员为其软件启用HTTPS。
As the self-signed certificates aren’t trustworthy, neither browsers nor standard HTTPS clients like OkHttp and Apache HTTP Client trust them by default.
由于自签名证书不值得信任,浏览器和标准的HTTPS客户端,如OkHttp和Apache HTTP客户端,都不默认信任它们。
Lastly, we can conveniently fetch the server certificate using a web browser or the OpenSSL command-line utility.
最后,我们可以方便地使用网络浏览器或OpenSSL命令行工具来获取服务器证书。
3. Setting Up the Test Environment
3.设置测试环境
To demonstrate an application accepting and trusting a self-signed certificate using OkHttp, let’s quickly configure and spin up a simple Spring Boot application with HTTPS enabled (secured by a self-signed certificate).
为了演示应用程序接受并信任使用OkHttp的自签名证书,让我们快速配置并启动一个简单的Spring Boot应用程序,启用HTTPS(由自签名证书担保)。
The default configuration will start a Tomcat server listening on port 8443 and expose a secured REST API accessible at “https://localhost:8443/welcome”.
默认配置将启动一个在8443端口监听的Tomcat服务器,并公开一个可在“https://localhost:8443/welcome”访问的安全REST API。
Now, let’s use an OkHttp client to make an HTTPS request to this server and consume the “/welcome” API.
现在,让我们使用一个OkHttp客户端向该服务器发出HTTPS请求,并消耗“/welcome” API。
4. OkHttpClient and SSL
4、OkHttpClient和SSL
This section will initialize an OkHttpClient and use it to connect to the test environment we just set up. Additionally, we’ll examine the errors encountered in our path, and step by step, reach our final goal of trusting a self-signed certificate using OkHttp.
本节将初始化一个OkHttpClient并使用它来连接到我们刚刚建立的测试环境。此外,我们将检查在我们的路径中遇到的错误,并一步一步地达到我们的最终目标,即使用OkHttp信任一个自签名的证书。
First, let create a builder for the OkHttpClient:
首先,让我们为OkHttpClient创建一个构建器。
OkHttpClient.Builder builder = new OkHttpClient.Builder();
Also, let’s declare the HTTPS URL that we’ll use throughout this tutorial:
此外,让我们声明我们将在本教程中使用的HTTPS URL。
int SSL_APPLICATION_PORT = 8443;
String HTTPS_WELCOME_URL = "https://localhost:" + SSL_APPLICATION_PORT + "/welcome";
4.1. The SSLHandshakeException
4.1.SSLHandshakeException
Without configuring the OkHttpClient for SSL, if we attempt to consume an HTTPS URL, we get a security exception:
如果没有为SSL配置OkHttpClient,如果我们试图消费一个HTTPS URL,我们会得到一个安全异常。
@Test(expected = SSLHandshakeException.class)
public void whenHTTPSSelfSignedCertGET_thenException() {
builder.build()
.newCall(new Request.Builder()
.url(HTTPS_WELCOME_URL).build())
.execute();
}
The stack trace is:
堆栈跟踪是。
javax.net.ssl.SSLHandshakeException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
...
The above error precisely means that the server uses a self-signed certificate that is not signed by a Certificate Authority (CA).
上述错误恰恰意味着服务器使用的是没有经过证书颁发机构(CA)签名的自签证书。
Therefore, the client could not verify the chain of trust up to the root certificate, so it threw an SSLHandshakeException.
因此,客户端无法验证信任链,直到根证书,所以它抛出了一个SSLHandshakeException。
4.2. The SSLPeerUnverifiedException
4.2.SSLPeerUnverifiedException
Now, let’s configure the OkHttpClient that trusts a certificate regardless of its nature – CA-signed or self-signed.
现在,让我们配置一下OkHttpClient,无论证书的性质如何–CA签名的还是自签名的,它都会被信任。
First, we need to create our own TrustManager that nullifies the default certificate validations and overrides those with our custom implementation:
首先,我们需要创建我们自己的TrustManager,使默认的证书验证无效,并用我们的自定义实现重写这些验证。
TrustManager TRUST_ALL_CERTS = new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
};
Next, we’ll use the above TrustManager to initialize an SSLContext, and also set the OkHttpClient builder’s SSLSocketFactory:
接下来,我们将使用上面的TrustManager来初始化一个SSLContext,同时设置OkHttpClient构建程序的SSLSocketFactory。
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { TRUST_ALL_CERTS }, new java.security.SecureRandom());
builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) TRUST_ALL_CERTS);
Again, let’s run the test. It won’t be hard to believe that even after the above tweaks, consuming the HTTPS URL throws an error:
再次,让我们运行测试。不难相信,即使经过上述调整,消费HTTPS URL也会抛出一个错误。
@Test(expected = SSLPeerUnverifiedException.class)
public void givenTrustAllCerts_whenHTTPSSelfSignedCertGET_thenException() {
// initializing the SSLContext and set the sslSocketFactory
builder.build()
.newCall(new Request.Builder()
.url(HTTPS_WELCOME_URL).build())
.execute();
}
The exact error is:
确切的错误是。
javax.net.ssl.SSLPeerUnverifiedException: Hostname localhost not verified:
certificate: sha256/bzdWeeiDwIVjErFX98l+ogWy9OFfBJsTRWZLB/bBxbw=
DN: CN=localhost, OU=localhost, O=localhost, L=localhost, ST=localhost, C=IN
subjectAltNames: []
This is due to a well-known problem – the hostname verification failure. Most of the HTTP libraries perform hostname verification against the certificate’s SubjectAlternativeName’s DNS Name field, which is unavailable in the server’s self-signed certificate, as seen in the detailed stack trace above.
这是由于一个众所周知的问题 – 主机名验证失败。大多数HTTP库针对证书的SubjectAlternativeName的DNS Name字段进行主机名验证,这在服务器的自签名证书中是不可用的,正如上面的详细堆栈跟踪所见。
4.3. Overriding the HostnameVerifier
4.3.重写HostnameVerifier
The last step towards configuring the OkHttpClient correctly is to disable the default HostnameVerifier and replace it with another one that bypasses the hostname verification.
正确配置OkHttpClient的最后一步是禁用默认的HostnameVerifier,并用另一个绕过主机名验证的方法取代它。
Let’s put in this last chunk of customization:
让我们把这最后一大块定制的东西放进去。
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
Now, let’s run our test one last time:
现在,让我们最后一次运行我们的测试。
@Test
public void givenTrustAllCertsSkipHostnameVerification_whenHTTPSSelfSignedCertGET_then200OK() {
// initializing the SSLContext and set the sslSocketFactory
// set the custom hostnameVerifier
Response response = builder.build()
.newCall(new Request.Builder()
.url(HTTPS_WELCOME_URL).build())
.execute();
assertEquals(200, response.code());
assertNotNull(response.body());
assertEquals("<h1>Welcome to Secured Site</h1>", response.body()
.string());
}
Finally, the OkHttpClient is successfully able to consume the HTTPS URL secured by a self-signed certificate.
最后,OkHttpClient能够成功地消费由自签名证书担保的HTTPS URL。
5. Conclusion
5.总结
In this tutorial, we learned about configuring SSL for an OkHttpClient such that it’s able to trust a self-signed certificate and consume any HTTPS URL.
在本教程中,我们学习了如何为OkHttpClient配置SSL,以便它能够信任一个自签名的证书并消费任何HTTPS URL。
However, an important point to consider is that, although this design entirely omits certificate validation and hostname verification, all communications between the client and the server are still encrypted. The trust between the two parties is lost, but the SSL handshake and the encryption aren’t compromised.
然而,需要考虑的一个重要问题是,尽管这种设计完全省略了证书验证和主机名验证,但客户端和服务器之间的所有通信仍然是加密的。双方之间的信任会丢失,但SSL握手和加密并没有受到影响。
As always, we can find the complete source code over on GitHub.
一如既往,我们可以在GitHub上找到完整的源代码超过。