欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android OKHttp源碼解析Https安全處理

 更新時間:2022年12月05日 15:09:33   作者:ZSAchg  
這篇文章主要為大家介紹了Android OKHttp源碼解析Https安全處理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

Https

Https是Http協(xié)議加上下一層的SSL/TSL協(xié)議組成的,TSL是SSL的后繼版本,差別很小,可以理解為一個東西。進行Https連接時,會先進行TSL的握手,完成證書認證操作,產(chǎn)生對稱加密的公鑰、加密套件等參數(shù)。之后就可以使用這個公鑰進行對稱加密了。

Https的加密方式同時使用了非對稱加密和對稱加密:

  • 使用反向的非對稱加密對證書進行簽名
  • 在檢查通過的證書公鑰基礎(chǔ)上,利用非對稱加密產(chǎn)生對稱加密的公鑰
  • 使用產(chǎn)生的公鑰,利用對稱加密交互傳輸?shù)臄?shù)據(jù)。

上面就是Https工作的大致流程,下面詳細介紹下加密的知識和握手。

加密知識

對于加密這種技術(shù),很早很早之前就有了。沒有加密的數(shù)據(jù),稱為明文,經(jīng)過加密的叫做密文。Http默認都是明文傳輸,這種方式很容易被監(jiān)聽或者修改。加密的最終目的,是保證機密性、完整性、可用性。

密碼是一套加密算法,使用計算機之前都是使用機械式后者密碼本進行操作。在使用計算機之后,加密的安全程度愈來越高,但是被解密也愈來愈容易。

秘鑰

如果光有密碼和原始數(shù)據(jù),那么破解會簡單很多,因為只要知道了密碼的加密方式,反向操作即可拿到明文數(shù)據(jù)。所以為了增加難度,增加了秘鑰。

現(xiàn)在密碼就需要兩個參數(shù)進行計算了,秘鑰+明文+密碼==密文。只拿到密碼和密文,是不能獲取原始明文,還需要秘鑰。破解的難度就更大了。

用秘鑰加密的技術(shù),又因為加密和解密秘鑰的情況分兩種。

對稱加密

加密和解密的秘鑰是相同的,這種加密方式被稱為對稱加密,使用的秘鑰被稱為公鑰。

對稱加密的速度很快,但是服務(wù)器需要把自己的公鑰傳到客戶端,客戶端使用這個公鑰對數(shù)據(jù)進行加密,服務(wù)器使用同樣的公鑰進行解密。

但是這種方式?jīng)]有辦法防止中間人攻擊,如果中間人篡改了傳輸?shù)墓€,使用自己的公鑰代替他,那么接可以截取發(fā)送方的數(shù)據(jù),使用自己的公鑰進行解密。

非對稱加密

加密和解密的秘鑰是不同的,這種加密方式被稱為非對稱加密,進行加密的是公共的公鑰,而進行解密的叫做私鑰。這樣只有私鑰的擁有者才可以解密數(shù)據(jù)。

反向的非對稱加密是數(shù)字簽名,也就是使用私鑰進行加密,使用公共的公鑰的進行解密,這樣就可以鑒定發(fā)送者的身份。也就是只有發(fā)送者才有私鑰。

非對稱加密的缺點是速度很慢,同樣也沒有辦法防止中間人攻擊,中間人截獲了服務(wù)器的公鑰。并用自己的公鑰代替,這樣也可以獲取發(fā)送者的數(shù)據(jù)。

Https的方案

因為對稱和非對稱加密都有自己的問題,都是因為公鑰的傳遞沒法保證安全性,中間人可以通過替換成自己的公鑰,完成截取的工作。

Https使用了混合的方式,同時使用了兩種方式,使用非對稱加密產(chǎn)生對稱加密的公鑰,再通過對稱加密進行處理。首先對稱加密比較快速相對于非對稱加密。所以還是使用對稱加密比較好,那么對稱加密的缺點時怎么保證公鑰能夠安全的交換呢。

這里可以使用非對稱加密傳輸這段公鑰。這樣這段公鑰就可以被安全的傳輸。因為只有服務(wù)器的私鑰才可以進行解密。非對稱加密有什么問題呢,就是不能判斷收到的公鑰是否就是真正的公鑰,是不是被篡改或者替換。怎么保證受到的公鑰就是合法的公鑰呢?

那就需要一個機構(gòu)來給這個公鑰背書,可以通過它保證這個公鑰是合法的,而承載公鑰的載體就是證書??蛻舳送ㄟ^證書進行驗證,完成公鑰的獲取。之后就可以通過這個公鑰完成非對稱加密傳輸。協(xié)商對稱加密所用的公鑰。

Https的方案大體就是這樣。 以上就是Https的基礎(chǔ)知識。下面分析下TSL的握手細節(jié)。

TSL握手

TSL的握手主要的目的要協(xié)商加密的算法、對稱加密的公鑰、TSL/SSL版本。

首先通過連接到服務(wù)器的443端口,通過TCP連接,這段是明文傳輸,用于溝通上面所說的參數(shù)。建立完成連接后就可以開始進行握手操作了。

握手如上圖所示,逐條分析下

  • 客戶端發(fā)送 client hello的報文,發(fā)送了客戶端支持的協(xié)議版本、密碼套件、隨機數(shù)、壓縮算法等,服務(wù)器要在這之中選中一個自己支持的,如果自己都不支持,那么就會斷開連接。
  • 服務(wù)器返回 server hello報文,內(nèi)含選中的版本、密碼套件、隨機數(shù)、壓縮算法等。并會返回自己的證書。
  • 客戶端收到證書后,檢驗這個證書,檢驗分四步:時間有效性檢查、簽發(fā)的頒發(fā)者可信度檢測、簽名檢測、站點名稱檢測。如果四項檢測都通過了,那么就會取出證書中的公鑰。
  • 通過上面產(chǎn)生的隨機數(shù),產(chǎn)生了Pre-master secret,該報文使用從證書中解密獲得的公鑰進行加密(其實就是服務(wù)器的公鑰),并通過公鑰加密傳輸?shù)椒?wù)端。通過這個數(shù)通過DH算法計算出MAC報文摘要和對稱加密的公鑰。 上面的方式就產(chǎn)生了可以進行對稱加密的公鑰。下面發(fā)送的數(shù)據(jù)就可以通過這個公鑰開始對稱加密了。

沒有用到數(shù)字證書? 傳輸?shù)淖C書使用了數(shù)字證書也就是反向的對稱加密,當收到證書,檢驗通過后,會使用CA的公鑰進行檢測,也就是CA使用了自己的私鑰進行了加密,只有CA知道私鑰。
隨機數(shù)怎么計算的?可以參考這里

隨機數(shù)計算

傳輸過程中,會涉及3個隨機數(shù),客戶端產(chǎn)生的/服務(wù)端產(chǎn)生的/Pre-master secret。 在傳輸Pre-master secret時,會使用從證書獲取的公開秘鑰,只有服務(wù)器才可以解密,對于客戶端:

當其生成了Pre-master secret之后,會結(jié)合原來的A、B隨機數(shù),用DH算法計算出一個master secret,緊接著根據(jù)這個master secret推導出hash secretsession secret。

對于服務(wù)端:
當其解密獲得了Pre-master secret之后,會結(jié)合原來的A、B隨機數(shù),用DH算法計算出一個master secret,緊接著根據(jù)這個master secret推導出hash secretsession secret

在客戶端和服務(wù)端的master secret是依據(jù)三個隨機數(shù)推導出來的,它是不會在網(wǎng)絡(luò)上傳輸?shù)模挥须p方知道,不會有第三者知道。同時,客戶端推導出來的session secrethash secret與服務(wù)端也是完全一樣的。

那么現(xiàn)在雙方如果開始使用對稱算法加密來進行通訊,使用哪個作為共享的密鑰呢?過程是這樣子的:

雙方使用對稱加密算法進行加密,用hash secret對HTTP報文 做一次運算生成一個MAC,附在HTTP報文的后面,然后用session-secret加密所有數(shù)據(jù)(HTTP+MAC),然后發(fā)送。

接收方則先用session-secret解密數(shù)據(jù),然后得到HTTP+MAC,再用相同的算法計算出自己的MAC,如果兩個MAC相等,證明數(shù)據(jù)沒有被篡改。

OkHttp的設(shè)計

OkHttp是支持自動的Https連接的,也就是我們默認訪問一個Https的網(wǎng)站,會自動的完成TSL的握手和加密。但是對于自簽名的證書還是需要我們進行配置的。

涉及的類

  • ConnectionSpec :連接的參數(shù)配置,包括SSL/TLS的版本、密碼套件等,這個在OkHttpClient#connectionSpecs進行配置,默認是具有SNI和ALPN等擴展功能的現(xiàn)代TLS和clear text即明文傳輸。SSL握手的前兩部就是溝通這部分參數(shù)的。
  • CertificateChainCleaner:證書鏈清理工具,用于省略無用的證書,過濾出一個列表,最后一個鏈結(jié)是受信任的證書。
  • X509TrustManager:此接口的實例管理哪些 X509 證書可用于驗證安全套接字的遠程端。 決策可能基于受信任的證書頒發(fā)機構(gòu)、證書撤銷列表、在線狀態(tài)檢查或其他方式。這個類對應上面證書檢測的簽發(fā)的頒發(fā)者可信度檢測、簽名檢測、時間有效性檢查。
  • HostnameVerifier:驗證主機名是否與服務(wù)器的身份驗證方案匹配??梢曰谧C書,也可以基于其他方式。這個用于上面說的驗證證書的站點名稱檢測。
  • X509Certificate:X.509 證書的抽象類。 這提供了一種訪問 X.509 證書所有屬性的標準方法。現(xiàn)有的證書都是X509類型的,這時一個標準。
  • SSLSocketFactory:這個是jdk提供的工具,負責SSLSocket,SSLSocket可以調(diào)用handShake進行ssl的握手。
  • CertificatePinner:固定證書配置,用于對握手通過的證書做固定驗證,也就是證書必須滿足固定證書的配置。 上面的類不但有jdk還有OkHttp的工具,共同完成了Https的工作。OkHttp大部分利用了jdk關(guān)注Https的支持。

OkHttpClient配置階段

OkHttpClient作為OkHttp的入口,可以對上面的類進行配置??聪略赽uidler里是怎么進行配置的。

單獨配置SSLSocketFactory

設(shè)置用于保護 HTTPS 連接的套接字工廠。如果未設(shè)置,將使用系統(tǒng)默認值。

public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) {
  if (sslSocketFactory == null) throw new NullPointerException("sslSocketFactory == null");
  this.sslSocketFactory = sslSocketFactory;
  this.certificateChainCleaner = Platform.get().buildCertificateChainCleaner(sslSocketFactory);
  return this;
}

同時配置SSLSocketFactory和X509TrustManager

可以通過sslSocketFactory方法,配置上面的兩個參數(shù),正常情況下,我們不需要配置,只需要采用系統(tǒng)默認的配置即可。

public Builder sslSocketFactory(
    SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
  if (sslSocketFactory == null) throw new NullPointerException("sslSocketFactory == null");
  if (trustManager == null) throw new NullPointerException("trustManager == null");
  this.sslSocketFactory = sslSocketFactory;
  this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
  return this;
}

配置HostnameVerifier

可以通過hostnameVerifier()配置hostnameVerifier,以達到我們檢測證書的站點名稱。

public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) {
  if (hostnameVerifier == null) throw new NullPointerException("hostnameVerifier == null");
  this.hostnameVerifier = hostnameVerifier;
  return this;
}

HostnameVerifier是一個接口,我們只要調(diào)用它的verify方法就可以完成校驗,這個操作發(fā)生在Https握手完成后,系統(tǒng)提供了AbstractVerifier骨架類進行配置。默認是OkHostnameVerifier

配置CertificatePinner

設(shè)置固定證書,我們可以創(chuàng)建一個CertificatePinner,CertificatePinner是一個實現(xiàn)好的類,我們只要傳入主機名稱和證書的SHA-256或者SHA-1 hashes即可。握手守信的證書必須通過配置的固定證書。如果不滿足,就會拋出異常,停止鏈接。

public Builder certificatePinner(CertificatePinner certificatePinner) {
  if (certificatePinner == null) throw new NullPointerException("certificatePinner == null");
  this.certificatePinner = certificatePinner;
  return this;
}

OkHttpClient參數(shù)處理階段

上面我們可以通過builder配置參數(shù),那么參數(shù)是如何進行處理的呢。我們配置不配置一個參數(shù)又有什么不同呢?

boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
  isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
  // 自定義或不使用Https
  this.sslSocketFactory = builder.sslSocketFactory;
  this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
  // 默認配置
  X509TrustManager trustManager = Util.platformTrustManager();
  this.sslSocketFactory = newSslSocketFactory(trustManager);
  this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
  Platform.get().configureSslSocketFactory(sslSocketFactory);
}

參數(shù)的處理代碼如上所示,先獲取鏈接的配置是否是TSL,除了明文連接外,都是使用TSL的。如果我們配置了自己的sslSocketFactory或者不是TSL連接(沒有配置sslSocketFactory),那么都會使用builde人內(nèi)部的sslSocketFactory。也就是說配置了,就使用配置的,沒有配置,如果當前不支持TSL,那么sslSocketFactory就為空。

如果沒有配置并且是TSL連接的話,這里就會使用默認的配置。這里的邏輯是先獲取X509TrustManager,再通過X509TrustManager獲取SslSocketFactory,通過SslSocketFactory再獲取CertificateChainCleaner。整體的依賴關(guān)系就是這樣。后面配置自定義證書時,也會使用這個依賴鏈。 依次看下每個過程:

X509TrustManager trustManager = Util.platformTrustManager();

public static X509TrustManager platformTrustManager() {
  try {
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
        TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init((KeyStore) null);
    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
    if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
      throw new IllegalStateException("Unexpected default trust managers:"
          + Arrays.toString(trustManagers));
    }
    return (X509TrustManager) trustManagers[0];
  } catch (GeneralSecurityException e) {
    throw assertionError("No System TLS", e); // The system has no TLS. Just give up.
  }
}

通過TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm());獲取默認的TrustManager工廠,調(diào)用init方法,這個方法傳入秘鑰庫,并使用證書頒發(fā)機構(gòu)和相關(guān)信任材料的來源初始化此工廠。通常使用傳入的KeyStore作為做出信任決策的基礎(chǔ)。我們自簽名的證書也會通過這個方法進行配置。

后面只餓極取出trustManagerFactory.getTrustManagers(),拿數(shù)組第一個作為最終的X509TrustManager。

this.sslSocketFactory = newSslSocketFactory(trustManager);

private static SSLSocketFactory newSslSocketFactory(X509TrustManager trustManager) {
  try {
    SSLContext sslContext = Platform.get().getSSLContext();
    sslContext.init(null, new TrustManager[] { trustManager }, null);
    return sslContext.getSocketFactory();
  } catch (GeneralSecurityException e) {
    throw assertionError("No System TLS", e); // The system has no TLS. Just give up.
  }
}

獲取X509TrustManager后,這里通過Platform.get().getSSLContext()獲取SSLContext。

Platform.get()通過反射獲取了不同平臺的配置工具,這樣OKHttp就可以運行在不同的平臺上。獲取SSLContext后,就可以init方法,對SSLContext進行配置,調(diào)用getSocketFactory獲取最終的SSLSocketFactory。

this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);

這里配置了CertificateChainCleaner,獲取trustManager中的可以用于驗證對等方的證書,之后創(chuàng)建一個BasicCertificateChainCleaner。

通過上面的兩個配置的步驟,就完成了配置階段,看看在連接時是怎么使用Https的。

OkHttp連接Https階段

Https的握手發(fā)生在Http連接之后,在ConnectInterceptor這個連接攔截器中。在調(diào)用完connectSocket后,就開始進行SSL的握手。因為Https需要默認連接443 端口,但是Http會連接80端口,這個邏輯是在哪兒配置的呢。在我們構(gòu)建請求的Request傳入的HttpUrl中,有一個port字段就是用于確定端口的。在獲取端口時,如果沒有進行顯式的配置。就會根據(jù)defaultPort()進行配置。邏輯也比較簡單。所以connectSocket會直接連接443端口,為下面的SSL握手做了準備。

public static int defaultPort(String scheme) {
  if (scheme.equals("http")) {
    return 80;
  } else if (scheme.equals("https")) {
    return 443;
  } else {
    return -1;
  }
}

進行SSL連接主要通過connectTls進行。 通過下面的方法進行判斷。如果sslSocketFactory不為null,那么就會使用Https進行連接。

if (route.address().sslSocketFactory() == null)
private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException {
  Address address = route.address();
  SSLSocketFactory sslSocketFactory = address.sslSocketFactory();
  boolean success = false;
  SSLSocket sslSocket = null;
  try {
    // 創(chuàng)建SSLSocket,是對原始Socke的包裝
    sslSocket = (SSLSocket) sslSocketFactory.createSocket(
        rawSocket, address.url().host(), address.url().port(), true /* autoClose */);
    // 配置SSL版本和密碼套件
    ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket);
    if (connectionSpec.supportsTlsExtensions()) {
      // 配置SSL擴展
      Platform.get().configureTlsExtensions(
          sslSocket, address.url().host(), address.protocols());
    }
    // 進行握手
    sslSocket.startHandshake();
    // 等待握手完成
    SSLSession sslSocketSession = sslSocket.getSession();
    Handshake unverifiedHandshake = Handshake.get(sslSocketSession);
    // 進行證書域名確定
    if (!address.hostnameVerifier().verify(address.url().host(), sslSocketSession)) {
      List<Certificate> peerCertificates = unverifiedHandshake.peerCertificates();
      if (!peerCertificates.isEmpty()) {
        X509Certificate cert = (X509Certificate) peerCertificates.get(0);
        throw new SSLPeerUnverifiedException(
            "Hostname " + address.url().host() + " not verified:"
                + "\n    certificate: " + CertificatePinner.pin(cert)
                + "\n    DN: " + cert.getSubjectDN().getName()
                + "\n    subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert));
      } else {
        throw new SSLPeerUnverifiedException(
            "Hostname " + address.url().host() + " not verified (no certificates)");
      }
    }
    // 檢測固定證書
    address.certificatePinner().check(address.url().host(),
        unverifiedHandshake.peerCertificates());
    // 握手成功,獲取Http協(xié)議
    String maybeProtocol = connectionSpec.supportsTlsExtensions()
        ? Platform.get().getSelectedProtocol(sslSocket)
        : null;
    socket = sslSocket;
    source = Okio.buffer(Okio.source(socket));
    sink = Okio.buffer(Okio.sink(socket));
    handshake = unverifiedHandshake;
    protocol = maybeProtocol != null
        ? Protocol.get(maybeProtocol)
        : Protocol.HTTP_1_1;
    success = true;
  } catch (AssertionError e) {
    if (Util.isAndroidGetsocknameError(e)) throw new IOException(e);
    throw e;
  } finally {
    if (sslSocket != null) {
      Platform.get().afterHandshake(sslSocket);
    }
    if (!success) {
      closeQuietly(sslSocket);
    }
  }
}

邏輯比較清晰,逐條分析下

  • 通過sslSocketFactory創(chuàng)建SSLSocket。通過SSLSocket可以直接進行執(zhí)行SSL的握手。
  • 傳入上面講到的TSL版本和密碼套件
  • 配置SSL的擴展,這里如果ALPN的擴展,會寫上使用的Http版本,握完手后會取這個配置,并判斷是否使用Http2.0版本。
  • 調(diào)用sslSocket.startHandshake(),進行握手。這時一個同步的操作,會阻塞當前線程,直到握手成功,如果中間出了什么問題,那么會直接拋出異常。
  • 完成握手后會獲取Handshake數(shù)據(jù),執(zhí)行到這里說明握手已經(jīng)成功了,服務(wù)器的證書已經(jīng)被信任了。證實的信息就在Handshake中。

通過我們傳域名檢測完成域名檢測,也就是hostnameVerifier類,調(diào)用它的verify方法,通過返回的boolean值,進行判斷。默認的值時OkHostnameVerifier。verify方法實現(xiàn)如下。這里檢測了host和ip的值。如果不一致,可能證書被替換了。

public boolean verify(String host, X509Certificate certificate) {
  return verifyAsIpAddress(host)
      ? verifyIpAddress(host, certificate)
      : verifyHostname(host, certificate);
}
  • 通過CertificatePinner固定證書檢測,調(diào)用check進行檢測。如果當前受信的證書不滿足固定的配置,那么就不能繼續(xù)請求。固定證書的威力很大,如果配置了,那么后續(xù)的版本必須滿足這個固定的配置,所以一直要商量好。
  • 所有的檢查通過,握手成功。這時就是獲取配置的時候了。比如商議的Http版本和連接成功的輸入輸出流,之后的傳輸,也會通過SSlSocket的輸入輸出進行配置了。 以上就完成了SSL的握手和配置。

在實際應用中我們可能需要配置自己的證書,如果完全使用CA的證書,我們是不需要配置什么的,使用默認配置即可,但是還是有些場景需要自己動手配置Https。最常見的情形就是配置自簽名的證書,服務(wù)器給我們一個根證書,我們配置在本地,在握手階段,服務(wù)器給出的證書,會受這個根證書的認證。這樣既完成了自簽名證書的配置。下面是一些場景和常用的OkHttp的代碼配置。

配置自簽名證書

信任所有證書

這是一種非常不安全的配置,這么配置,會導致毫無安全性可言。但是有些場景還是可以暫時使用的。

static class HttpsTrustAllCertsTrustManager implements X509TrustManager {
  @Override
  public void checkClientTrusted(X509Certificate[] chain, String authType)
      throws CertificateException {
  }
  @Override
  public void checkServerTrusted(X509Certificate[] chain, String authType)
      throws CertificateException {
  }
  @Override
  public X509Certificate[] getAcceptedIssuers() {
    return new X509Certificate[0]; //返回長度為0的數(shù)組,相當于return null
  }
  public static SSLSocketFactory createSSLSocketFactory() {
    SSLSocketFactory sSLSocketFactory = null;
    try {
      SSLContext sc = Platform.get().getSSLContext();
      sc.init(null, new TrustManager[]{new HttpsTrustAllCertsTrustManager()},new SecureRandom());
      sSLSocketFactory = sc.getSocketFactory();
    } catch (Exception e) {
    }
    return sSLSocketFactory;
  }
}
static class TrustAllHostnameVerifier implements HostnameVerifier {
  @Override
  public boolean verify(String s, SSLSession sslSession) {
    return true;
  }
}
//構(gòu)建OkHttpClient
OkHttpClient mClient =
    new OkHttpClient.Builder()
        .sslSocketFactory(HttpsTrustAllCertsTrustManager
            .createSSLSocketFactory(), new HttpsTrustAllCertsTrustManager())
        .hostnameVerifier(new TrustAllHostnameVerifier())
        .build();

上面共配置了兩個變量,SslSocketFactory和HostnameVerifier。

  • 第一個變量依賴X509TrustManager。這個認證中心,我們需要給一個空實現(xiàn),這樣就會信任所有的證書,創(chuàng)建的模式和OkHttpClient創(chuàng)建默認的配置套路一樣。
  • 第二個HostnameVerifier,如果我們不進行配置,會走一個默認的OkHostnameVerifier,如果不設(shè)置也會驗證域名。所以還需要實現(xiàn)一個自定義的驗證期,永遠返回true。

這樣經(jīng)過兩個兩步的設(shè)置,就完成了所有證書的配置工作。這種模式可以配合固定證書使用,也就是服務(wù)器的證書只能滿足固定的規(guī)則才可以,也不失是一種策略。

配置自簽名證書

對于自簽名的證書,一般都是一個根證書,服務(wù)器返回的證書,使用這個根證書就可以進行認證。我們的任務(wù)就是配置這個默認的自簽名證書進入OkHttp的配置。

try {
  CertificateFactory cf = CertificateFactory.getInstance("X.509");
  //獲取證書輸入流
  InputStream caInput = null;
  Certificate ca;
  try {
      ca = cf.generateCertificate(caInput);
  } finally {
      caInput.close();
  }
  // 創(chuàng)建KeyStore,穿入證書
  String keyStoreType = KeyStore.getDefaultType();
  KeyStore keyStore = KeyStore.getInstance(keyStoreType);
  keyStore.load(null, null);
  keyStore.setCertificateEntry("ca", ca);
  // 創(chuàng)建TrustManagerFactory,用于生成TrustManager
  TrustManagerFactory tmf =
      TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
  tmf.init(keyStore);
  SSLContext s = SSLContext.getInstance("TLSv1", "AndroidOpenSSL");
  s.init(null, tmf.getTrustManagers(), null);
  return s.getSocketFactory();
} catch (Exception e) {
  e.printStackTrace();
} 

上面信任所有證書,我們只是自己實現(xiàn)了一個TrustManager,但是在配置自簽名證書的時候,就需要通過TrustManagerFactory獲取了。和上面配置的主要區(qū)別,也在于TrustManager的創(chuàng)建。

  • 獲取證書的輸入流,構(gòu)建Certificate
  • 獲取KeyStore,通過傳入證書Certificate
  • 創(chuàng)建TrustManagerFactory,并調(diào)用init,初始化KeyStore
  • 通過SSLContext的init方法獲取SSLSocketFactory

通過傳入的SSLSocketFactory,傳入OkHttpClient就可以了。整體邏輯還是比較簡單的。

以上就是Android OKHttp源碼解析Https安全處理的詳細內(nèi)容,更多關(guān)于Android OKHttp Https安全處理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論