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