-
中間人攻擊
我們都知道使用fiddler抓取app的數(shù)據(jù)包,不管是http還是https請求,都能輕松抓取,此時(shí)對客戶端來說,fiddler是一個(gè)服務(wù)端,對服務(wù)端來說,fiddler就變成了一個(gè)客戶端,查了下資料,這種方式稱為“中間人攻擊”,怎么才能防止https中的“中間人攻擊”呢,工作中也用到了https,所以想深入研究一下這個(gè)問題,當(dāng)然每個(gè)問題如果深挖的話,都需要很多知識的支持,所以這個(gè)過程有些地方是自己的理解,難免有些偏差,有問題,咱們討論區(qū)見。
-
單向認(rèn)證
平時(shí)我們說的單項(xiàng)認(rèn)證,一般指的是客戶端對服務(wù)端的認(rèn)證,當(dāng)客戶端向服務(wù)端發(fā)送請求時(shí),服務(wù)端會把自己的證書信息發(fā)給客戶端,這個(gè)證書信息包括服務(wù)端的公鑰、有效時(shí)間、有效地址和CA的數(shù)字簽名等信息,所以客戶端需要與預(yù)埋一個(gè)證書,這樣我們可以拿本地證書和服務(wù)端發(fā)送的證書進(jìn)行信息匹配,完成認(rèn)證的過程(在使用fiddler抓包時(shí),需要事先安裝的證書就是為了完成這個(gè)客戶端對服務(wù)端的認(rèn)證過程,而且這個(gè)證書應(yīng)該不是正規(guī)的CA機(jī)構(gòu)頒發(fā)的證書,而是fiddler自己生成的證書)。
-
android中單向認(rèn)證的過程
1.獲取客戶端預(yù)埋的服務(wù)器端的證書對象
InputStream mCaInputStream =context.getResources().openRawResource(R.raw.server_cert);
2.生成符合x509標(biāo)準(zhǔn)的證書
CertificateFactory mCertificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate certificate =(X509Certificate) mCertificateFactory.generateCertificate(mCaInputStream);
if (mCaInputStream != null) {
mCaInputStream.close();
}
3.將證書導(dǎo)入到本地的證書密鑰庫中去
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
//這幾行代碼,貌似沒有導(dǎo)入的操作?
keyStore.load(null, null);
keyStore.setCertificateEntry("123", certificate);
4.使用本地密鑰庫初始化信任管理器中去
TrustManagerFactory trustManagerFactory= TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
5.使用信任管理器得到X509TrustManager
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
//這個(gè)位置,我直接取了數(shù)組的第一個(gè)元素,貌似不妥。
X509TrustManager x509TrustManager = (X509TrustManager) trustManagers[0];
6.使用X509TrustManager校驗(yàn)服務(wù)端的證書,此方法不報(bào)異常即使校驗(yàn)成功
x509TrustManager.checkServerTrusted(chain, authType);
-
分析過程
- 主要分析第6步,這步中的x509TrustManager對象,是我使用客戶端預(yù)埋的證書生成的,用這個(gè)證書去校驗(yàn)服務(wù)器返回來的證書,如果校驗(yàn)成功則客戶端對服務(wù)端認(rèn)證成功,否則,失敗。
- 這里如果失敗了還有個(gè)處理,就是在預(yù)埋證書校驗(yàn)失敗的情況下,我們重新初始化了一個(gè)系統(tǒng)默認(rèn)的信任管理器TrustManagerFactory,加載手機(jī)端的所有證書到信任管理器中,再去校驗(yàn)服務(wù)端的證書是否是信任的,如果校驗(yàn)成功則客戶端對服務(wù)端認(rèn)證成功,否則,校驗(yàn)失敗,此時(shí)才是真正的認(rèn)證失敗。不過這個(gè)處理比較危險(xiǎn),具體分析見下一條:
- 我的實(shí)際情況是這樣的,服務(wù)器使用的是阿里云提供的CA證書,這種證書是經(jīng)過專門的CA機(jī)構(gòu)頒發(fā)的,理論上講不需要客戶端代碼認(rèn)證,此時(shí)如果客戶端證書跟這個(gè)阿里云證書不匹配,因?yàn)槭褂昧耸謾C(jī)中默認(rèn)的信任管理器TrustManagerFactory參與校驗(yàn),校驗(yàn)也是會通過校驗(yàn)的,同理,fiddler抓包也是能夠抓取的,也就是我們這種認(rèn)證失去了作用了。
....
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
// 發(fā)生異常,就用設(shè)備本身默認(rèn)的信任管理器進(jìn)行校驗(yàn)
// 這樣可能存在危險(xiǎn),經(jīng)過測試如果加上這句話,fiddler可以正常抓包的
// 這是因?yàn)閒iddler抓包之前,會在客戶端安裝一個(gè)證書,如果指定的證書校驗(yàn)失敗
// 就是默認(rèn)使用這個(gè)證書匹配,結(jié)果能夠和fiddler匹配成功。
try {
TrustManagerFactory trustManagerFactory
= TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
//初始化手機(jī)本身默認(rèn)的證書信任管理器用于認(rèn)證。
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
X509TrustManager x509TrustManager = chooseTrustManager(trustManagers);
x509TrustManager.checkServerTrusted(chain, authType);
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
} catch (KeyStoreException e1) {
e1.printStackTrace();
}
- 所以在實(shí)際中,需要保證認(rèn)證的有效期,一般不會啟動本地默認(rèn)的TrustManagerFactory對象參與校驗(yàn)
-
OkHttp中的ssl配置
- 校驗(yàn)域名
public class UnSafeHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
if (AppConstants.HOST_NAME.equals(hostname)) {
//第一請求時(shí),會返回域名hostname,用于和本地域名校驗(yàn)
return true;
}
return false;
}
}
- 調(diào)用SSLSocketFactory和X509TrustManager對象
SSLHelper5 sslHelper5 = new SSLHelper5(App.getContext());
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.sslSocketFactory(sslHelper5.provideSSLSocketFactory()
, sslHelper5.provideX509TrustManager())
.hostnameVerifier(new UnSafeHostnameVerifier())
-
項(xiàng)目文件
-
完成(關(guān)于Https的雙向認(rèn)證,近期完成)