OkHttp尝试平衡两个相互竞争的要素:html
连通性(Connectivity):链接到尽量多的服务器。这包括运行最新版本 boringssl 的服务器和不太过期的老版本 OpenSSL 的服务器。java
链接的安全性(Security):这包括远程web服务器证书验证,和对私密数据交换的强加密。git
在与HTTPS服务器协商一个链接时,OkHttp须要知道提供哪一种TLS版本(TLS versions)和密码套件(cipher suites)。一个但愿最大化连通性的客户端会包含废弃的TLS版本和弱设计的密码套件。一个但愿最大化安全性的严格的客户端将会限制只使用最新的TLS版本和最强的密码套件。github
具体的安全性 vs 连通性的决定是由ConnectionSpec实现的。OkHttp包含三种内置的链接策略:web
MODERN_TLS
是链接到最新的HTTPS服务器的安全配置。COMPATIBEL_TLS
是链接到过期的HTTPS服务器的安全配置。CLEARTEXT
是用于http://开头的URL的非安全配置。 默认状况下,OkHttp将会尝试MODERN_TLS
链接,若是当前配置失败,会退回到COMPATIBLE_TLS
链接。浏览器
每种链接策略中,具体的TLS版本和密码套件在每一个版本中均可能会变。例如,在OkHttp 2.2中,咱们禁用了SSL 3.0,以应对POODLE攻击;在Ok Http2.3中咱们禁用了RC4。同你的桌面web浏览器同样,使用最新的OkHttp版本是保证安全的最好方法。安全
你能够创建本身的链接策略,使用自定义的TLS版本和密码套件。例如,下面的配置限制在三种被高度重视的密码套件。它的缺陷是须要Android 5.0以上,和相应的新web服务器。服务器
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 = ... client.setConnectionSpecs(Collections.singletonList(spec));
默认状况下,OkHttp信任运行平台支持的证书颁发机构。这种策略最大化了连通性,但它受到对认证机构的攻击的制约,例如2011年的DigiNotar攻击。它也假定了你的HTTPS服务器的证书是由证书颁发机构签名的。ide
使用CertificatePinner来约束哪些认证机构被信任。证书锁定增长了安全性,但限制了你的服务器团队升级TLS证书的能力。没有来自服务器TLS管理员的祝福,不要使用证书锁定!ui
public CertificatePinning() { client = new OkHttpClient(); client.setCertificatePinner( new CertificatePinner.Builder() .add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=") .add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=") .add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=") .add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=") .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)); } }
完整的代码展现了如何用你本身的设置替换运行平台支持的认证机构。同上,没有来自服务器TLS管理员的祝福,不要使用证书锁定!
private final OkHttpClient client; public CustomTrust() { client = new OkHttpClient(); SSLContext sslContext = sslContextForTrustedCertificates(trustedCertificatesInputStream()); client.setSslSocketFactory(sslContext.getSocketFactory()); } 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. }