HTTPS請(qǐng)求為什么就安全砾莱?
https是一個(gè)建立在密碼學(xué)基礎(chǔ)之上的一種安全通信協(xié)議事扭,準(zhǔn)確來說是基于HTTP協(xié)議和SSL/TSL的組合,要想理解HTTPS必須了解密碼學(xué)的一些相關(guān)基礎(chǔ)概念。
公鑰密碼體制
公鑰密碼體制分為三個(gè)部分焚鲜,公鑰,私鑰放前,加密解密算法忿磅,加密解密過程如下:
- 加密:通過加密算法和公鑰對(duì)內(nèi)容(或者說明文)進(jìn)行加密,得到密文凭语。加密過程需要用到公鑰葱她。
- 解密:通過解密算法和私鑰對(duì)密文進(jìn)行解密,得到明文似扔。解密過程需要用到解密算法和私鑰吨些。注意,由公鑰加密的內(nèi)容炒辉,只能由私鑰進(jìn)行解密豪墅,也就是說,由公鑰加密的內(nèi)容黔寇,如果不知道私鑰偶器,是無法解密的。
對(duì)稱加密算法
在對(duì)稱加密算法中啡氢,加密使用的密鑰和解密使用的密鑰是相同的状囱。也就是說,加密和解密都是使用的同一個(gè)密鑰倘是。因此對(duì)稱加密算法要保證安全性的話亭枷,密鑰要做好保密,只能讓使用的人知道搀崭,不能對(duì)外公開叨粘。這個(gè)和上面的公鑰密碼體制有所不同,公鑰密碼體制中加密是用公鑰瘤睹,解密使用私鑰升敲,而對(duì)稱加密算法中,加密和解密都是使用同一個(gè)密鑰轰传,不區(qū)分公鑰和私鑰驴党。 密鑰,一般就是一個(gè)字符串或數(shù)字获茬,在加密或者解密時(shí)傳遞給加密/解密算法港庄。前面在公鑰密碼體制中說到的公鑰倔既、私鑰就是密鑰,公鑰是加密使用的密鑰鹏氧,私鑰是解密使用的密鑰渤涌。
RSA簡介
RSA密碼體制是一種公鑰密碼體制,公鑰公開把还,私鑰保密实蓬,它的加密解密算法是公開的。 由公鑰加密的內(nèi)容可以并且只能由私鑰進(jìn)行解密吊履,并且由私鑰加密的內(nèi)容可以并且只能由公鑰進(jìn)行解密安皱。也就是說,RSA的這一對(duì)公鑰艇炎、私鑰都可以用來加密和解密练俐,并且一方加密的內(nèi)容可以由并且只能由對(duì)方進(jìn)行解密。
非對(duì)稱加密算法
在非對(duì)稱加密算法中冕臭,加密使用的密鑰和解密使用的密鑰是不相同的。前面所說的公鑰密碼體制就是一種非對(duì)稱加密算法燕锥,他的公鑰和是私鑰是不能相同的辜贵,也就是說加密使用的密鑰和解密使用的密鑰不同,因此它是一個(gè)非對(duì)稱加密算法归形。
了解了以上的基礎(chǔ)概念之后我們就可以聊聊為什么安全了托慨,這里我們就可以籠統(tǒng)的講一下當(dāng)我們打開一個(gè)https鏈接的時(shí)候發(fā)生了什么事情。
- 當(dāng)我們?cè)谕ㄐ胚^程開始時(shí)暇榴,會(huì)收到服務(wù)器的證書厚棵。
- 首先應(yīng)用程序讀取證書中的Issuer(發(fā)布機(jī)構(gòu)),然后會(huì)在應(yīng)用程序的系統(tǒng)中受信任的發(fā)布機(jī)構(gòu)的證書中去找蔼紧。
- 如果找不到婆硬,那證書可能有問題,程序會(huì)給出一個(gè)錯(cuò)誤信息奸例。
- 如果在系統(tǒng)中找到這個(gè)證書的發(fā)布機(jī)構(gòu)彬犯,那么應(yīng)用程序就會(huì)從證書中取出這個(gè)機(jī)構(gòu)證書的公鑰。
- 然后對(duì)服務(wù)器下發(fā)的證書里面的指紋和指紋算法用這個(gè)公鑰進(jìn)行解密查吊,然后使用指紋算法計(jì)算服務(wù)器下發(fā)證書中的指紋谐区。將這個(gè)計(jì)算的指紋與放在證書中的指紋對(duì)比。(非對(duì)稱加密)
- 如果一致說明服務(wù)器下發(fā)的證書是通過通過機(jī)構(gòu)發(fā)布的逻卖。然后就可以保證通信安全了宋列。
- 然后這時(shí)候應(yīng)用程序選擇一個(gè)對(duì)稱加密算法和一個(gè)秘鑰,用公鑰加密之后發(fā)送給服務(wù)器评也。
- 服務(wù)器收到后使用私鑰解密炼杖,獲取秘鑰灭返,本次通話會(huì)從現(xiàn)在開始,通過對(duì)稱加密算法進(jìn)行通信嘹叫。
通過上訴簡單的描述婆殿,也可了解到https通信是是通過各種加密算法及證書比對(duì)校驗(yàn)完成的一個(gè)http通信。
SSL/TLS證書罩扇?
ssl Secure Sockets Layer,現(xiàn)在應(yīng)該叫"TLS",但由于習(xí)慣問題,我們還是叫"SSL"比較多婆芦,ssl證書是數(shù)字證書的一種,數(shù)字證書有較多的內(nèi)容項(xiàng)喂饥,里面的內(nèi)容比較多——Version消约、Serial number、Signature algorithm 等等员帮,挑幾個(gè)重要的解釋一下或粮。
- Issuer (證書的發(fā)布機(jī)構(gòu))
指出是什么機(jī)構(gòu)發(fā)布的這個(gè)證書,也就是指明這個(gè)證書是哪個(gè)公司創(chuàng)建的(只是創(chuàng)建證書捞高,不是指證書的使用者) - Valid from , Valid to (證書的有效期)
也就是證書的有效時(shí)間氯材,或者說證書的使用期限。 過了有效期限硝岗,證書就會(huì)作廢氢哮,不能使用了。 - Public key (公鑰)
這個(gè)我們?cè)谇懊娼榻B公鑰密碼體制時(shí)介紹過型檀,公鑰是用來對(duì)消息進(jìn)行加密的 - Subject (主題)
這個(gè)證書是發(fā)布給誰的冗尤,或者說證書的所有者,一般是某個(gè)人或者某個(gè)公司名稱胀溺、機(jī)構(gòu)的名稱裂七、公司網(wǎng)站的網(wǎng)址等。 對(duì)于這里的證書來說仓坞,證書的所有者是Trustwave這個(gè)公司背零。 - Signature algorithm (簽名所使用的算法)
就是指的這個(gè)數(shù)字證書的數(shù)字簽名所使用的加密算法,這樣就可以使用證書發(fā)布機(jī)構(gòu)的證書里面的公鑰扯躺,根據(jù)這個(gè)算法對(duì)指紋進(jìn)行解密捉兴。指紋的加密結(jié)果就是數(shù)字簽名。 - Thumbprint, Thumbprint algorithm (指紋以及指紋算法)
這個(gè)是用來保證證書的完整性的录语,也就是說確保證書沒有被修改過倍啥。 其原理就是在發(fā)布證書時(shí),發(fā)布者根據(jù)指紋算法(一個(gè)hash算法)計(jì)算整個(gè)證書的hash值(指紋)并和證書放在一起澎埠,使用者在打開證書時(shí)虽缕,自己也根據(jù)指紋算法計(jì)算一下證書的hash值(指紋),如果和剛開始的值對(duì)得上蒲稳,就說明證書沒有被修改過,因?yàn)樽C書的內(nèi)容被修改后,根據(jù)證書的內(nèi)容計(jì)算的出的hash值(指紋)是會(huì)變化的爽彤。 注意,這個(gè)指紋會(huì)使用"SecureTrust CA"這個(gè)證書機(jī)構(gòu)的私鑰用簽名算法(Signature algorithm)加密后和證書放在一起诉植。
證書頒發(fā)機(jī)構(gòu)(CA)
·Symantec (which bought VeriSign's SSL interests and owns Thawte and Geotrust) 38.1% 市場份額
·Comodo SSL 29.1%
·Go Daddy 13.4%
·GlobalSign 10%
安卓系統(tǒng)中, 你可以在下列路徑中找到證書頒發(fā)機(jī)構(gòu): 設(shè)置 -> 安全 -> 受信任的憑證
最佳和最安全的私鑰獲取方法是向可信任的證書頒發(fā)機(jī)構(gòu) (CA)(例如將帶您完成身份驗(yàn)證過程的賽門鐵克公司)申請(qǐng)一個(gè)證書。一旦有了自己的證書昵观,就可以生成您自己的私鑰晾腔。您可以使用該私鑰為您的可執(zhí)行文件或軟件庫簽名,私鑰僅可通過可追溯到 CA 的公鑰解鎖啊犬,而公鑰已預(yù)安裝在大部分瀏覽器中灼擂。如果在簽名后代碼被篡改,公鑰將不能核實(shí)私鑰簽名的真實(shí)性觉至,瀏覽器將立即向任何嘗試下載代碼的人彈出警告窗口剔应。如果代碼完好無損,則將提供您的文语御。
什么是自簽名證書峻贮?
自簽名證書就是沒有通過受信任的證書頒發(fā)機(jī)構(gòu), 自己給自己頒發(fā)的證書.
SSL 證書大致分三類:
- 由安卓認(rèn)可的證書頒發(fā)機(jī)構(gòu)(如: VeriSign), 或這些機(jī)構(gòu)的下屬機(jī)構(gòu)頒發(fā)的證書.
- 沒有得到安卓認(rèn)可的證書頒發(fā)機(jī)構(gòu)頒發(fā)的證書.
- 自己頒發(fā)的證書, 分臨時(shí)性的(在開發(fā)階段使用)或在發(fā)布的產(chǎn)品中永久性使用的兩種.
X.509 又是什么
X.509 - 這是一種證書標(biāo)準(zhǔn),主要定義了證書中應(yīng)該包含哪些內(nèi)容.其詳情可以參考RFC5280,SSL/TLS使用的就是這種證書標(biāo)準(zhǔn).
編碼格式
同樣的X.509證書,可能有不同的編碼格式,目前有以下兩種編碼格式.
PEM - Privacy Enhanced Mail,打開看文本格式,以"-----BEGIN..."開頭, "-----END..."結(jié)尾,內(nèi)容是BASE64編碼.
查看PEM格式證書的信息:openssl x509 -in certificate.pem -text -noout
Apache和*NIX服務(wù)器偏向于使用這種編碼格式.
DER - Distinguished Encoding Rules,打開看是二進(jìn)制格式,不可讀.
查看DER格式證書的信息:openssl x509 -in certificate.der -inform der -text -noout
Java和Windows服務(wù)器偏向于使用這種編碼格式.
KeyStore
在 Android 和 java中 更多的是使用keystore。最常用的工具就是keytool.
Keytool是一個(gè)Java數(shù)據(jù)證書的管理工具 ,Keytool將密鑰(key)和證書(certificates)存在一個(gè)稱為keystore的文件中应闯。
在keystore里月洛,包含兩種數(shù)據(jù):
- 密鑰實(shí)體(KeyEntry)——密鑰(secret key)
- 可信任的證書實(shí)體(CertEntry)——只包含公鑰
每個(gè)keystore都關(guān)聯(lián)這一個(gè)獨(dú)一無二的alias,這個(gè)alias通常不區(qū)分大小寫
Android 中如何配置SSL/TLS
以上所述皆是對(duì)HTTPS SSL/TLS的概念敘述孽锥,通過這個(gè)描述會(huì)讓你對(duì)下面的配置以及編碼更加了解。我們?cè)贏ndroid 開發(fā)中配置項(xiàng)目支持HTTPS這是個(gè)基本的操作细层。下面我會(huì)以O(shè)kHttp網(wǎng)絡(luò)框架為例惜辑。通過以下三點(diǎn)可完成項(xiàng)目配置。
- 如何配置系統(tǒng)疫赎、用戶信任證書校驗(yàn)盛撑?
- 自簽名證書如何在項(xiàng)目中配置?
- 如何通過配置文件進(jìn)行配置SSL/TLS證書捧搞?
如何配置系統(tǒng)抵卫、用戶信任證書校驗(yàn)?
Android OkHttp框架默認(rèn)是提供了對(duì)系統(tǒng)用戶信任證書的校驗(yàn)胎撇。如果服務(wù)器下發(fā)的證書是被Android信任機(jī)構(gòu)頒發(fā)的證書是不會(huì)出現(xiàn)安全提示的介粘,或者是用戶添加到系統(tǒng)的證書也是不會(huì)出現(xiàn)任何安全提示。
在 AOSP 源碼庫中晚树,CA 根證書主要存放在 system/ca-certificates 目錄下姻采,而在 Android 系統(tǒng)中,則存放在 /system/etc/security/ 目錄下爵憎。所以我們沒有Root的手機(jī)是無法進(jìn)行添加根證書的慨亲。
也可以根據(jù)以下配置進(jìn)行系統(tǒng)根證書安全校驗(yàn)
public static SSLSocketFactory getSSLSocketFactory() {
SSLSocketFactory ssfFactory = null;
SSLContext sc = null;
try {
sc = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
sc.init((KeyManager[]) null, trustManagerFactory.getTrustManagers(), new SecureRandom());
ssfFactory = sc.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
return ssfFactory;
}
特定的 CA 證書如何配置婚瓜?
不論是權(quán)威機(jī)構(gòu)頒發(fā)的證書還是自簽名的,打包一份到 app 內(nèi)部刑棵,比如存放在 asset 里巴刻,下面的示例展示了此過程:從 InputStream 獲取一個(gè)特定的 CA,用該 CA 創(chuàng)建 KeyStore蛉签,然后用后者創(chuàng)建和初始化 TrustManager胡陪。TrustManager 是系統(tǒng)用于驗(yàn)證來自服務(wù)器的證書的工具,可以使用一個(gè)或多個(gè) CA 從 KeyStore創(chuàng)建正蛙,而創(chuàng)建的 TrustManager 將僅信任這些 CA督弓。
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
以上代碼,可以解決證書信任問題乒验。但同時(shí)需要注意的是愚隧,這里是基于Android默認(rèn)的信任檢查來解決的。因?yàn)槲覀儧]有對(duì)TrustManager做任何修改锻全,如果仍然遇到證書校驗(yàn)不通過的情況狂塘,則需要重新實(shí)現(xiàn)TrustManager,請(qǐng)用以下代碼代替“tmf.getTrustManagers()”:
鳄厌。這時(shí)候我們需要通過自定義的校驗(yàn)方式方可完成校驗(yàn)荞胡。通過自定義X509TrustManager來實(shí)現(xiàn)checkServerTrusted的校驗(yàn),其實(shí)就是通過上訴介紹的SSL/TLS校驗(yàn)的方式
context.init(null, new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
for (X509Certificate cert : chain) {
// Make sure that it hasn't expired.
cert.checkValidity();
// Verify the certificate's public key chain.
try {
cert.verify(((X509Certificate) ca).getPublicKey());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}, null);
先校驗(yàn)證書的日期是否過期了嚎,其次通過本地預(yù)置證書的公鑰對(duì)服務(wù)器下發(fā)的數(shù)字證書進(jìn)行對(duì)比校驗(yàn)泪漂。
如何通過文件配置證書?
Android P新特性歪泳,全面禁止了非安全的http連接萝勤,如果要使用非加密連接,需要配置network security config
步驟如下:
- 在res/xml下建立我們自己的network security config文件呐伞,名字任意敌卓,可以叫做network_security_config.xml
- 如果我們相對(duì)某些網(wǎng)址使用非安全連接,可以使用如下配置伶氢,以下可以說涵蓋了很多配置趟径。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
//用于輔助抓包,7.0以上證書的權(quán)限變了
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
//信任用戶證書
<certificates src="user"></certificates>
//信任用戶自己安裝的證書
<certificates src="system"></certificates>
//定義白名單
<certificates src="@raw/cn_area"></certificates>
</trust-anchors>
</base-config>
<!-- 使用自簽名 SSL 證書的主機(jī) 配置-->
<!-- cleartextTrafficPermitted="false" 強(qiáng)制校驗(yàn)https 否則可以允許通過http-->
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">example.com</domain> <!-- 過濾域名癣防,可配置多個(gè) -->
<domain includeSubdomains="true">example1.com</domain> <!-- 一般會(huì)將 CDN 配置在此 -->
<trust-anchors>
<!--這就是我們自己的證書-->
<!--如果要信任多個(gè)證書蜗巧,就可以寫多個(gè)-->
<certificates src="@raw/cn_area"/>
<!--也可以把這些證書放在一個(gè)目錄下-->
<certificates src="@raw/cn_area"/>
//信任用戶證書
<certificates src="user"></certificates>
//信任用戶自己安裝的證書
<certificates src="system"></certificates>
</trust-anchors>
<!-- 固定證書-->
<pin-set expiration="2018-01-01">
<pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
<!-- backup pin -->
<pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
</pin-set>
</domain-config>
<!-- debug 模式配置調(diào)試CA-->
<debug-overrides>
<trust-anchors>
//信任用戶證書
<certificates src="user"></certificates>
//信任用戶自己安裝的證書
<certificates src="system"></certificates>
</trust-anchors>
</debug-overrides>
</network-security-config>
下面方式可以通過具體的需求進(jìn)行具體的配置
如果我們的某個(gè)網(wǎng)址的證書是自簽名的證書,我們想要訪問這個(gè)網(wǎng)址蕾盯,可以進(jìn)行如下配置
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">example.com</domain>
<trust-anchors>
<!--這就是我們自己的證書-->
<!--如果要信任多個(gè)證書惧蛹,就可以寫多個(gè)-->
<certificates src="@raw/my_ca"/>
<!--也可以把這些證書放在一個(gè)目錄下-->
<certificates src="@raw/trusted_roots"/>
</trust-anchors>
</domain-config>
</network-security-config>
如果我們想讓App信任除系統(tǒng)之外的其他的CA,可以進(jìn)行如下配置
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<!--在base-config中配置額外的CA-->
<certificates src="@raw/extracas"/>
<certificates src="system"/>
</trust-anchors>
</base-config>
</network-security-config>
如果我們?cè)谡{(diào)試的時(shí)候staging服務(wù)器不是正規(guī)CA,我們可以進(jìn)行如下配置
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!--debug-overrides表示在調(diào)試狀態(tài)下信任的CA,當(dāng)android:debugable為true的時(shí)候,是調(diào)試狀態(tài)-->
<debug-overrides>
<trust-anchors>
<certificates src="@raw/debug_cas"/>
</trust-anchors>
</debug-overrides>
</network-security-config>
一般情況下香嗓,應(yīng)用信任所有預(yù)裝 CA迅腔。如果有預(yù)裝 CA 簽發(fā)欺詐性證書,則應(yīng)用將面臨被中間人攻擊的風(fēng)險(xiǎn)靠娱。有些應(yīng)用通過限制信任的 CA 集或通過固定證書沧烈,選擇限制其接受的證書集
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">example.com</domain>
<pin-set expiration="2018-01-01">
<pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
<!-- backup pin -->
<pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
</pin-set>
</domain-config>
</network-security-config>
更詳細(xì)的信息請(qǐng)參考Android Security Config
數(shù)字證書轉(zhuǎn)化https://blog.csdn.net/xiangguiwang/article/details/76400805