客戶端訪問https網站
一般有兩種方式實現,一是信任所有的證書,也就是跳過證書合法性校驗這一步驟,對于這種做法肯定是有風險的;二是校驗證書,證書合法才能訪問。
第一種方式 :信任所有證書
解決證書不被系統承認的方法,就是跳過系統校驗。要跳過系統校驗,就不能再使用系統標準的SSLSocketFactory了,需要自定義一個。然后為了在這個自定義的SSLSocketFactory里跳過校驗,還需要自定義一個X509TrustManager(Android采用的是X509驗證),建立我們的驗證規則,在其中忽略所有校驗,即TrustAll。
// okhttp不驗證繞過自己證書,使用android內置證書,直接通過所有的https連接
// 獲取這個SSLSocketFactory
public static SSLSocketFactory getSSLSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, getTrustManager(), new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
使用這樣的極端方式,雖然使用了HTTPS,實現了客戶端和服務器端的通信內容得到了加密,嗅探程序無法得到傳輸的內容,但引生出來一種弊端,無法抵擋“中間人攻擊”。例如,在內網配置一個DNS,把目標服務器域名解析到本地的一個地址,然后在這個地址上使用一個中間服務器作為代理,它使用一個假的證書與客戶端通訊,然后再由這個代理服務器作為客戶端連接到實際的服務器,用真的證書與服務器通訊。這樣所有的通訊內容都會經過這個代理,而客戶端不會感知,這是由于客戶端不校驗服務器公鑰證書導致的,如charles抓包。
第二種方式:檢驗證書
為了防止上面方案可能導致的“中間人攻擊”,我們可以下載服務器端公鑰證書,然后將公鑰證書編譯到Android應用中一般在assets文件夾保存,由應用在交互過程中去驗證證書的合法性。
//使用內置證書
public static SSLSocketFactory getSSLSocketFactory_Certificate(Context context) {
try {
//獲取X.509格式的內置證書
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
Collection<? extends Certificate> cers = certificateFactory.generateCertificates(context.getAssets().open("CACertificate.cer"));
int index = 0;
for (Certificate certificate : cers) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificate);
}
//用這個KeyStore去引導生成的TrustManager來提供驗證
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
//用TrustManager生成SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
//SSLContext返回SSLSocketFactory
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
這樣一來雖然抓包工具是無法攔截到我們的數據了,但作為我們平時使用能非常直接看到接口返回數據的工具不能用了,對我們也造成了不便?,F在我們想用Charles,如何防止中間人攻擊同時又只能被Charles抓到呢?
我們一起再來看一下Charles的原理。
為何Https依然會被截???
目前的一些抓包工具,fiddler、charles使用了叫做man-in-the-middle(中間人)機制:
具體簡述起來就是對客戶端設置Charles的代理,作為客戶端的代理,由Charles連接到實際服務器。
第一步, charles向服務器發送請求進行握手, 獲取到服務器的CA證書, 用根證書公鑰進行解密, 驗證服務器數據簽名,獲取到服務器CA證書公鑰。
第二步, charles偽造自己的CA證書, 冒充服務器證書傳遞給客戶端瀏覽器,客戶端做跟charles一樣的事。
第三步, 客戶端瀏覽器生成https通信用的對稱密鑰,用charles偽造的證書公鑰加密后傳遞給服務器, 被charles截獲。
第四步, charles將截獲的密文用自己偽造證書的私鑰解開,獲得https通信用的對稱密鑰。
第五步, charles將對稱密鑰用服務器證書公鑰加密傳遞給服務器, 服務器用私鑰解開后建立信任,握手完成, 用對稱密鑰加密消息, 開始通信。
第六步, charles接收到服務器發送的密文, 用對稱密鑰解開,獲得服務器發送的明文。再次加密, 發送給客戶端瀏覽器。
第七步, 客戶端向服務器發送消息, 用對稱密鑰加密, 被charles截獲后,解密獲得明文。
由于charles一直擁有通信用對稱密鑰, 所以在整個https通信過程中信息對其透明。也就是說charles向服務器冒充是客戶端,向客戶端又謊稱自己是服務器。
也就是說開啟Charles抓包以后,我們的客戶端實際上是和Charles建立了連接,服務器的CA證書已經被Charles攔截,取而代之是由Charles偽造的CA證書,冒充了服務器的證書傳遞給了客戶端。
所以我們讓Charles的CA證書也成為我們證書信任鏈中的一個
從 http://charlesproxy.com/getssl 下載Charles的CA證書,后綴名為.pem的文件,用記事本打開,將提取出來的證書拷貝到自己服務器證書的下面
大功告成,接下來打開Charles試一試吧!!