基礎(chǔ)概念
HTTP
HTTP協(xié)議工作于 客戶端-服務(wù)端架構(gòu)上。通常,由HTTP客戶端發(fā)起一個(gè)請(qǐng)求,建立一個(gè)到服務(wù)器指定端口(默認(rèn)是80端口)的TCP連接。HTTP服務(wù)器則在那個(gè)端口監(jiān)聽(tīng)客戶端的請(qǐng)求。一旦收到請(qǐng)求,服務(wù)器會(huì)向客戶端響應(yīng)返回一個(gè)狀態(tài),比如"HTTP/1.1 200 OK",以及返回的內(nèi)容,如請(qǐng)求的文件、錯(cuò)誤消息、或者其它信息。
HTTPS
HTTPS 全稱 HTTP over TLS,是對(duì)工作在一加密連接(TLS 或 SSL)上的常規(guī)HTTP協(xié)議的稱呼。

TLS 是在傳輸層上層的協(xié)議,應(yīng)用層的下層,作為一個(gè)安全層而存在,翻譯過(guò)來(lái)一般叫做傳輸層安全協(xié)議。

TLS 協(xié)議請(qǐng)查看 還沒(méi)寫(xiě)
證書(shū)及認(rèn)證過(guò)程請(qǐng)查看 證書(shū)、CA、證書(shū)信任鏈
對(duì) HTTP 而言,安全傳輸層是透明不可見(jiàn)的,應(yīng)用層僅僅當(dāng)做使用普通的 Socket 一樣使用 SSLSocket 。
TLS是基于 X.509 認(rèn)證,他假定所有的數(shù)字證書(shū)都是由一個(gè)層次化的數(shù)字證書(shū)認(rèn)證機(jī)構(gòu)發(fā)出,即 CA。
TLS 是獨(dú)立于 HTTP 的,任何應(yīng)用層的協(xié)議都可以基于 TLS 建立安全的傳輸通道,如 SSH 協(xié)議。
從抓包說(shuō)起
我們知道使用使用 HTTP網(wǎng)絡(luò)監(jiān)聽(tīng)工具(Fiddler,Charles等),都是需要在客戶端安裝根證書(shū)的(這里涉及證書(shū)的信任鏈,請(qǐng)查看 證書(shū)、CA、證書(shū)信任鏈)
而Android系統(tǒng)對(duì)于證書(shū)是分為,系統(tǒng)預(yù)裝證書(shū) 和 用戶安裝證書(shū)。

Android 6.0(API 級(jí)別 23) 及更低版本,應(yīng)用默認(rèn)信任 系統(tǒng)預(yù)裝證書(shū)和用戶安裝證書(shū)
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
Android 7.0(API 級(jí)別 24) 以后則默認(rèn)僅信任 系統(tǒng)預(yù)裝證書(shū)
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
cleartextTrafficPermitted 標(biāo)識(shí)是否允許明文傳輸
https://developer.android.com/training/articles/security-config#CustomTrust
所以如果應(yīng)用沒(méi)有做特殊的配置,在 7.0 以上設(shè)備是無(wú)法監(jiān)聽(tīng)到網(wǎng)絡(luò)請(qǐng)求的。
證書(shū)配置
我們可以通過(guò)編輯配置文件來(lái)修改app的默認(rèn)網(wǎng)絡(luò)安全配置
首先,需要 在 manifest 處聲明配置文件
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>
</manifest>
然后就是編輯配置文件,是個(gè)xml文件,數(shù)據(jù)結(jié)構(gòu)格式如下:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="..."/>
...
</trust-anchors>
</base-config>
<domain-config>
<domain>android.com</domain>
...
<trust-anchors>
<certificates src="..."/>
...
</trust-anchors>
<pin-set>
<pin digest="...">...</pin>
...
</pin-set>
</domain-config>
...
<debug-overrides>
<trust-anchors>
<certificates src="..."/>
...
</trust-anchors>
</debug-overrides>
</network-security-config>
-
base-config,默認(rèn)的配置,不在 domain-config 范圍內(nèi)的所有連接所使用的配置。
- <trust-anchors> 證書(shū)集合,可包裹多個(gè) <certificates> 證書(shū)
-
domain-config。滿足domain規(guī)則所使用的配置,可配置任意多個(gè),domain-config的嵌套表示繼承外層的配置規(guī)則。
- <trust-anchors> 證書(shū)集合
- <pin-set> 固定的證書(shū),通過(guò) expiration 配置過(guò)期時(shí)間,可包裹多個(gè) <pin> 證書(shū)
- <domain> 域名規(guī)則,通過(guò) includeSubdomains 配置是否支持子域名
- <domain-config> 嵌套規(guī)則
-
debug-overrides。android:debuggable = true使用,會(huì)將這個(gè)節(jié)點(diǎn)下的證書(shū)添加到其他配置上。
- <trust-anchors> 證書(shū)集合
以上比較重要的是 <certificates> 和 <pin> 這里特別提下他們的結(jié)構(gòu)
- <certificates>
// src 標(biāo)識(shí)證書(shū)(如果是raw資源則必須以 DER 或 PEM 格式編碼)
具體轉(zhuǎn)化請(qǐng)點(diǎn)擊查看
// overridePins 標(biāo)識(shí)是否繞過(guò)證書(shū)固定,默認(rèn)值為 "false",除非在 debug-overrides 有特別指定
<certificates src=["system" | "user" | "raw resource"]
overridePins=["true" | "false"] />
- <pin>
證書(shū)固定,需要 Base64編碼的公鑰SHA256摘要 Base64(SHA256(SubjectPublicKeyInfo))
此處懵逼,詳情請(qǐng)查閱 [https://stackoverflow.com/questions/40404963/how-do-i-get-public-key-hash-for-ssl-pinning]
<pin digest=["SHA-256"]>base64 encoded digest of X.509
SubjectPublicKeyInfo (SPKI)</pin>
通過(guò)命令可以獲取到 pin
openssl s_client -connect xxx.com:443 -servername xxx.com | openssl x509 -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
舉個(gè)栗子,我想debug 模式信任所有證書(shū),但release僅信任系統(tǒng)和特定的證書(shū)
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system"/>
<certificates src="@raw/ca_debug_charles"/>
</trust-anchors>
</base-config>
<debug-overrides>
<trust-anchors>
<certificates src="system"/>
<certificates src="user"/>
</trust-anchors>
</debug-overrides>
</network-security-config>
具體請(qǐng)查閱官網(wǎng)驗(yàn)證
https://developer.android.google.cn/training/articles/security-ssl
開(kāi)發(fā)者設(shè)置
這個(gè)時(shí)候我就有需求,我想要release的包是不信任用戶證書(shū)的,但是又需要留個(gè)后門(mén)給自己調(diào)試。
比如:輸入框輸入特定代碼證明我是開(kāi)發(fā)者后,網(wǎng)絡(luò)框架就取消對(duì)證書(shū)的驗(yàn)證,這樣就可以通過(guò)安裝用戶證書(shū)來(lái) 監(jiān)控調(diào)試 release包的https網(wǎng)絡(luò)請(qǐng)求。
代碼使用OkHttp實(shí)現(xiàn),其他網(wǎng)絡(luò)框架請(qǐng)自行g(shù)oogle。
/**
* 默認(rèn)信任所有的證書(shū)
*/
@SuppressLint("TrulyRandom")
private static SSLSocketFactory createSSLSocketFactory() {
SSLSocketFactory sSLSocketFactory = null;
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, new TrustManager[]{new TrustAllManager()},
new SecureRandom());
sSLSocketFactory = sc.getSocketFactory();
} catch (Exception e) {
}
return sSLSocketFactory;
}
private static class TrustAllManager 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];
}
}
private static class TrustAllHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(createSSLSocketFactory())
.hostnameVerifier(new TrustAllHostnameVerifier())
.build();
Reference
https://developer.android.com/training/articles/security-config
https://medium.com/@appmattus/android-security-ssl-pinning-1db8acb6621e
https://stackoverflow.com/questions/40404963/how-do-i-get-public-key-hash-for-ssl-pinning
http://www.itdecent.cn/p/59a102f150aa
作者:cchao1024
鏈接:http://www.itdecent.cn/p/dcfb61720413
來(lái)源:簡(jiǎn)書(shū)
簡(jiǎn)書(shū)著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。