首先,要先介紹一下https,相信大家都知道http,這是一個位于應(yīng)用層的網(wǎng)絡(luò)數(shù)據(jù)交互協(xié)議,那么很明顯,為什么要有https呢,這個“s”指的是什么意思呢?答:security,意思就是保證能夠安全地進行網(wǎng)絡(luò)數(shù)據(jù)交互。
讓我們來看看http的缺點:
1.無法驗證傳輸/接收方的身份 。
2.傳輸明文 。
3.無法保證數(shù)據(jù)的完整性,易被篡改。
最后來看看https為了解決這些問題而做的努力以及為此付出的代價:
1.服務(wù)器驗證:
驗證CA證書機構(gòu)身份,用公鑰解密出服務(wù)端的公鑰,同時要驗證服務(wù)端ip地址,hostname是否跟想要訪問的是一個服務(wù)器,如果是自簽的證書,需要針對證書身份做校驗,這就是需要客戶端里面放置cer證書的原因,如果不是自簽,按照原理說是不需要做驗證的,但是有些手機沒有內(nèi)置一些CA證書,所以最保險的保證兼容性的方法就是不管是不是自簽,都要做驗證。。
2.客戶端驗證:在服務(wù)端,針對客戶端進行身份驗證,驗證方法需要讀取客戶端的jks,并與cer作驗證。
3.數(shù)據(jù)完整性。
4.效率相對http較為低下。
5.加密。
6.普適性,都使用統(tǒng)一的協(xié)議。
接著,針對原理,對照圖示,解釋一下,https是如何做到這些的:

首先公司的服務(wù)器開發(fā)或者運營人員向CA機構(gòu)申請證書,并生成一對秘鑰(非對稱秘鑰,也就是分私鑰和公鑰),為了做一個區(qū)分,先把這個公鑰命名為公鑰P,將公鑰P交給CA機構(gòu),CA機構(gòu)在線下線上通過各種方法驗證組織真實存在,然后將證書里的明文信息(申請者公鑰、申請者組織信息和個人信息、CA機構(gòu)信息、證書有效時間、證書序列號等)進行hash,得到一個信息摘要,然后使用CA機構(gòu)的私鑰W對信息摘要進行解密得到數(shù)字簽名,客戶端通過握手得到服務(wù)器端發(fā)來的證書,首先受用相同的hash算法計算出明文信息的信息摘要A,再使用客戶端內(nèi)置的CA證書取出CA公鑰Z,用公鑰Z解密數(shù)字簽名得到信息摘要B,比對信息摘要A和信息摘要B,來確認證書的合法性。成功后,通過公鑰P加密master secret(隨機數(shù))后發(fā)送給服務(wù)器,服務(wù)器通過公鑰P對應(yīng)的私鑰Q來解密客戶端傳輸過來的加密過的master secret,得到master secret,再通過master secret、Server random、client random生成session key作為對稱秘鑰,使客戶端和服務(wù)器端進行通信。

解釋完原理,下面介紹使用okhttp是如何配置https驗證的:
第一步:通過工廠類生成一個證書Certificate
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = context.getResources().getAssets().open(name);
Certificate ca = cf.generateCertificate(caInput);
caInput.close();
第二步:通過ca初始化keyStore
if(keyStoreType ==null|| keyStoreType.length() ==0) {
keyStoreType = KeyStore.getDefaultType();
}
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null,null);
keyStore.setCertificateEntry("ca", ca);
第三步:通過keyStore初始化一個TrustManagerFactory
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
第四步:
final X509TrustManager wrappedTrustManagers = (X509TrustManager) tmf.getTrustManagers()[0];
return new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return originalTrustManager.getAcceptedIssuers();
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
try {
originalTrustManager.checkClientTrusted(certs, authType);
} catch (CertificateException e) {
e.printStackTrace();
}
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
try {
originalTrustManager.checkServerTrusted(certs, authType);
} catch (CertificateException e) {
e.printStackTrace();
}
}
};
第五步:使用TrustManager初始化一個SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, wrappedTrustManagers,null);
第六步:使用SSLSocketFactory sslSocketFactory和 X509TrustManager trustManager設(shè)置OkHttpClient.Builder的sslSocketFactory方法
client = new OkHttpClient();
OkHttpClient.Builder builder = client.newBuilder();
builder.sslSocketFactory(mySSLContext.getSslSocketFactory()/*,mySSLContext.getX509TrustManager()*/);
client = builder.build();
這樣就大功告成了,最后上面幾步是原理步驟,如有不懂,可以使用下面github上傳的源代碼:https://github.com/xfmax/TestOkhttp