OkHttp试图平衡两个相互冲突的问题:
- 尽可能多地连接到主机。其中包括运行最新版本的boringssl的高级主机,以及运行较旧版本的OpenSSL的过时主机。
- 安全连接。这包括使用证书和使用强密码保护交换数据的隐私,从而与远程WEB服务进行验证。
-
在与HTTPS服务器进行连接时,OkHttp需要知道需要提供哪个TLS版本和密码组。客户端如果想要最大化连接性则需要包括过时的TLS版本和弱设计的密码组。一个严格的客户端如果想要最大化安全性则需要被局限于使用最新的TLS版本和更强大的密码组。
OkHttp中具体的安全性和连接性是由ConnectionSpec实现决定的。OkHttp包括3个内置的连接规范:
- MODERN_TLS 是一个连接到现代HTTPS服务器的安全配置。
- COMPATIBLE_TLS 是一个连接到安全但非最新的HTTPS服务器的安全配置。
- CLEARTEXT 是一个只用于http://的URLS的不安全配置。
-
OkHttp默认会尝试使用MODERN_TLS 连接,如果现代配置失败了,则会回滚使用COMPATIBLE_TLS连接。
每个规范中的TLS版本和密码组可以在每个版本中进行更改。例如,在OkHttp2.2中针对POODLE的攻击,放弃了对SSL3.0的支持。在OkHttp2.3中放弃了对RC4的支持。与桌面WEB浏览器一样,保持最新版本的OkHttp是保持安全的最佳方式。
用户可以使用一个自定义的TLS版本和密码组来构建自己的连接规范。例如,下面代码中的配置仅限于三个高度重视的密码组。它的缺点就是它需要Android5.0+以上的版本和一个类似目前的现代网络服务器。ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .tlsVersions(TlsVersion.TLS_1_2) .cipherSuites( CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) .build(); OkHttpClient client = new OkHttpClient.Builder() .connectionSpecs(Collections.singletonList(spec)) .build();
Certificate Pinning 证书锁定
默认情况下,OkHttp信任主机平台的证书权威。这种策略最大化地保持了连接性,但是容易受到证书权威机构的攻击,例如2011年的DigiNotar攻击。它还假设你的HTTPS服务器中的证书是由证书权威机构签署的。
使用CertificatePinner来限制哪些证书和证书权威机构是受信任的。CertificatePinner提高了安全性,但是服务器团队被受限去更新它们的TLS证书。当你的服务器TLS管理员没有同意时,不能使用CertificatePinner。public CertificatePinning() { client = new OkHttpClient.Builder() .certificatePinner(new CertificatePinner.Builder() .add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=") .build()) .build(); } public void run() throws Exception { Request request = new Request.Builder() .url("https://publicobject.com/robots.txt") .build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); for (Certificate certificate : response.handshake().peerCertificates()) { System.out.println(CertificatePinner.pin(certificate)); } }
Customizing Trusted Certificates 定制受信任的证书
下面例子中完整的代码示例展示了怎么使用自己的设置来替换主机平台的证书权威机构。注意,当服务器TLS管理员没有同意时不能使用自定义证书。
private final OkHttpClient client; public CustomTrust() { SSLContext sslContext = sslContextForTrustedCertificates(trustedCertificatesInputStream()); client = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory()) .build(); } public void run() throws Exception { Request request = new Request.Builder() .url("https://publicobject.com/helloworld.txt") .build(); Response response = client.newCall(request).execute(); System.out.println(response.body().string()); } private InputStream trustedCertificatesInputStream() { ... // Full source omitted. See sample. } public SSLContext sslContextForTrustedCertificates(InputStream in) { ... // Full source omitted. See sample. }