okhttp翻译计划 (五)--HTTPS

第五部分:HTTPS

HTTPS

OkHttp会尝试平衡以下两个问题:

  • 尽可能多的连接主机的能力.这包括运行在新版本的boringssl的主机和运行旧版本的Openssl的过时的主机.
  • 连接的私密性.这包括使用证书来与远程服务器进行验证和使用强密码来保证数据交换的私密性.

当与一个HTTPS服务器协定一个连接时,OkHttp需要知道使用哪个版本的TLS(TLS version)和加密套件.一个客户端想要最大限度地提高连接能力就需要包括过时的TLS版本和弱设计的加密套件.一个加密的客户端想要最大限度地提高私密性就要被限制只使用最新版本的TLS和强加密套件.

在实现时,选择私密性还是连接能力取决于ConnectionSpec.OkHttp包括了三个嵌入式的连接方案:

  • MODERN_TLS是一个连接现代HTTPS服务器的安全配置.
  • COMPATIBLE_TLS是一个能够可靠连接非现代HTTPS服务器的安全配置
  • CLEARTEXT用来连接http://URLs的不安全的配置.

默认情况下,OkHttp会先尝试进行MODERN_TLS连接,如果失败就会进行COMPATIBLE_TLS连接.

在OkHttp的每个发行版中,每个方案的TLS版本和加密套件都会发生改变.比如,在OkHttp2.2中我们停止了对SSL3.0的支持以应对POODLE攻击.在OkHttp2.3中,我们停止了对RC4的支持.就像你的桌面网络浏览器一样,保持安全的最好办法就是保持对OkHttp的更新.

你可以使用一个定制的TLS版本和加密套件来构建你自己的连接方案.比如,下面的这个配置就是被限制使用了三重高级的加密套件.它的缺点就是需要工作在安卓5.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 DigNotar attack.OkHttp同时也假设你的HTTPS服务器的证书是被发证中心签名过的.

你可以使用CertificatePinner来限制哪个证书和发证中心会被信任.证书固定会增加保密性,但是会限制你的服务器团队更新TLS证书的能力.不要在没经过你们服务器的TLS管理员的允许的情况下使用证书固定!

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.
  }