HTTPS

OkHttp尽力平衡两个对立的关注点:

  • Connectivity :尽可能多的连接到不同的主机。包括使用最新版本 boringssl 的先进的主机,也包括那些使用老版本OpenSSL的相对落后的主机。
  • Security :链接的安全。包括远端服务使用证书验证和数据交换的加密。

在和一个HTTPS服务协商时,OkHttp需要知道使用哪个版本的TLS和 cipher suites。客户端想要最大的连接,要包括过时的TLS版本和低强度的加密算法。想要更安全的客户端需要使用最新的TLS版本和最强的加密算法。

具体的安全和连接决策是由 ConnectionSpec 来实现的。OkHttp包括四种内置的链接规则:

  • RESTRICTED_TLS 是安全的配置,是为了满足最严格的需求。
  • MODERN_TLS 是安全的配置,可以链接到现代的HTTPS。
  • COMPATIBLE_TLS 是安全的配置,可以链接到安全但不是现代HTTPS的服务。
  • CLEARTEXT 是不安全的配置,主要用来链接 http://

这些大致上是根据 Google Cloud Policies 提出的模型设计的。我们会跟踪这个规定的变化。

默认的,OkHttp会尝试MODERN_TLS 链接。但是,如果这个模式失败,通过配置客户端的connectionSpecs,你可以降级到 COMPATIBLE_TLS链接。

  1. OkHttpClient client = new OkHttpClient.Builder()
  2. .connectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS))
  3. .build();

每个规则的TLS版本和加密算法会随着每次发布的所变更。比如,OkHttp 2.2中,我们为了应对 POODLE 攻击,不再支持SSL3.0。在OkHttp 2.3 我们不再支持 RC4。和你的桌面浏览器一样,使用最新的OkHttp是最安全的。

你也可以使用一套自定义的TLS版本号和加密算法来实现你自己的链接规则。比如,这个配置限制使用三种高级的加密算法。缺点是需要使用Android 5.0+ 和一个现在的服务。

  1. ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
  2. .tlsVersions(TlsVersion.TLS_1_2)
  3. .cipherSuites(
  4. CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
  5. CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
  6. CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
  7. .build();
  8. OkHttpClient client = new OkHttpClient.Builder()
  9. .connectionSpecs(Collections.singletonList(spec))
  10. .build();

Certificate Pinning

默认的,OkHttp信任主机平台的证书。这个策略可以最大化链接,但是受限于安全认证中心攻击,比如 2011 DigiNotar attack。它也假定你的HTTPS服务证书是由安全认证中心颁发的。

使用 CertificatePinner 来限制哪些证书和哪些安全认证中心是可信的。证书锁定提升了安全性,但是限制了服务团队更新他们TLS证书的能力。不要在没有告知服务TLS领导就使用证书锁定。

  1. public CertificatePinning() {
  2. client = new OkHttpClient.Builder()
  3. .certificatePinner(new CertificatePinner.Builder()
  4. .add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
  5. .build())
  6. .build();
  7. }
  8. public void run() throws Exception {
  9. Request request = new Request.Builder()
  10. .url("https://publicobject.com/robots.txt")
  11. .build();
  12. Response response = client.newCall(request).execute();
  13. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  14. for (Certificate certificate : response.handshake().peerCertificates()) {
  15. System.out.println(CertificatePinner.pin(certificate));
  16. }
  17. }

Customizing Trusted Certificates

下边的例子演示了如何替换平台的安全认证中心成自己的。不要在没有通知服务TLS领导前就自定义证书。

  1. private final OkHttpClient client;
  2. public CustomTrust() {
  3. SSLContext sslContext = sslContextForTrustedCertificates(trustedCertificatesInputStream());
  4. client = new OkHttpClient.Builder()
  5. .sslSocketFactory(sslContext.getSocketFactory())
  6. .build();
  7. }
  8. public void run() throws Exception {
  9. Request request = new Request.Builder()
  10. .url("https://publicobject.com/helloworld.txt")
  11. .build();
  12. Response response = client.newCall(request).execute();
  13. System.out.println(response.body().string());
  14. }
  15. private InputStream trustedCertificatesInputStream() {
  16. ... // Full source omitted. See sample.
  17. }
  18. public SSLContext sslContextForTrustedCertificates(InputStream in) {
  19. ... // Full source omitted. See sample.
  20. }