
HTTPS請求為什么就安全?
https是一個建立在密碼學(xué)基礎(chǔ)之上的一種安全通信協(xié)議,準(zhǔn)確來說是基于HTTP協(xié)議和SSL/TSL的組合,要想理解HTTPS必須了解密碼學(xué)的一些相關(guān)基礎(chǔ)概念。
公鑰密碼體制
公鑰密碼體制分為三個部分,公鑰,私鑰,加密解密算法,加密解密過程如下:
- 加密:通過加密算法和公鑰對內(nèi)容(或者說明文)進行加密,得到密文。加密過程需要用到公鑰。
- 解密:通過解密算法和私鑰對密文進行解密,得到明文。解密過程需要用到解密算法和私鑰。注意,由公鑰加密的內(nèi)容,只能由私鑰進行解密,也就是說,由公鑰加密的內(nèi)容,如果不知道私鑰,是無法解密的。
對稱加密算法
在對稱加密算法中,加密使用的密鑰和解密使用的密鑰是相同的。也就是說,加密和解密都是使用的同一個密鑰。因此對稱加密算法要保證安全性的話,密鑰要做好保密,只能讓使用的人知道,不能對外公開。這個和上面的公鑰密碼體制有所不同,公鑰密碼體制中加密是用公鑰,解密使用私鑰,而對稱加密算法中,加密和解密都是使用同一個密鑰,不區(qū)分公鑰和私鑰。 密鑰,一般就是一個字符串或數(shù)字,在加密或者解密時傳遞給加密/解密算法。前面在公鑰密碼體制中說到的公鑰、私鑰就是密鑰,公鑰是加密使用的密鑰,私鑰是解密使用的密鑰。
RSA簡介
RSA密碼體制是一種公鑰密碼體制,公鑰公開,私鑰保密,它的加密解密算法是公開的。 由公鑰加密的內(nèi)容可以并且只能由私鑰進行解密,并且由私鑰加密的內(nèi)容可以并且只能由公鑰進行解密。也就是說,RSA的這一對公鑰、私鑰都可以用來加密和解密,并且一方加密的內(nèi)容可以由并且只能由對方進行解密。
非對稱加密算法
在非對稱加密算法中,加密使用的密鑰和解密使用的密鑰是不相同的。前面所說的公鑰密碼體制就是一種非對稱加密算法,他的公鑰和是私鑰是不能相同的,也就是說加密使用的密鑰和解密使用的密鑰不同,因此它是一個非對稱加密算法。
了解了以上的基礎(chǔ)概念之后我們就可以聊聊為什么安全了,這里我們就可以籠統(tǒng)的講一下當(dāng)我們打開一個https鏈接的時候發(fā)生了什么事情。
- 當(dāng)我們在通信過程開始時,會收到服務(wù)器的證書。
- 首先應(yīng)用程序讀取證書中的Issuer(發(fā)布機構(gòu)),然后會在應(yīng)用程序的系統(tǒng)中受信任的發(fā)布機構(gòu)的證書中去找。
- 如果找不到,那證書可能有問題,程序會給出一個錯誤信息。
- 如果在系統(tǒng)中找到這個證書的發(fā)布機構(gòu),那么應(yīng)用程序就會從證書中取出這個機構(gòu)證書的公鑰。
- 然后對服務(wù)器下發(fā)的證書里面的指紋和指紋算法用這個公鑰進行解密,然后使用指紋算法計算服務(wù)器下發(fā)證書中的指紋。將這個計算的指紋與放在證書中的指紋對比。(非對稱加密)
- 如果一致說明服務(wù)器下發(fā)的證書是通過通過機構(gòu)發(fā)布的。然后就可以保證通信安全了。
- 然后這時候應(yīng)用程序選擇一個對稱加密算法和一個秘鑰,用公鑰加密之后發(fā)送給服務(wù)器。
- 服務(wù)器收到后使用私鑰解密,獲取秘鑰,本次通話會從現(xiàn)在開始,通過對稱加密算法進行通信。
通過上訴簡單的描述,也可了解到https通信是是通過各種加密算法及證書比對校驗完成的一個http通信。
SSL/TLS證書?
ssl Secure Sockets Layer,現(xiàn)在應(yīng)該叫"TLS",但由于習(xí)慣問題,我們還是叫"SSL"比較多,ssl證書是數(shù)字證書的一種,數(shù)字證書有較多的內(nèi)容項,里面的內(nèi)容比較多——Version、Serial number、Signature algorithm 等等,挑幾個重要的解釋一下。
- Issuer (證書的發(fā)布機構(gòu))
指出是什么機構(gòu)發(fā)布的這個證書,也就是指明這個證書是哪個公司創(chuàng)建的(只是創(chuàng)建證書,不是指證書的使用者) - Valid from , Valid to (證書的有效期)
也就是證書的有效時間,或者說證書的使用期限。 過了有效期限,證書就會作廢,不能使用了。 - Public key (公鑰)
這個我們在前面介紹公鑰密碼體制時介紹過,公鑰是用來對消息進行加密的 - Subject (主題)
這個證書是發(fā)布給誰的,或者說證書的所有者,一般是某個人或者某個公司名稱、機構(gòu)的名稱、公司網(wǎng)站的網(wǎng)址等。 對于這里的證書來說,證書的所有者是Trustwave這個公司。 - Signature algorithm (簽名所使用的算法)
就是指的這個數(shù)字證書的數(shù)字簽名所使用的加密算法,這樣就可以使用證書發(fā)布機構(gòu)的證書里面的公鑰,根據(jù)這個算法對指紋進行解密。指紋的加密結(jié)果就是數(shù)字簽名。 - Thumbprint, Thumbprint algorithm (指紋以及指紋算法)
這個是用來保證證書的完整性的,也就是說確保證書沒有被修改過。 其原理就是在發(fā)布證書時,發(fā)布者根據(jù)指紋算法(一個hash算法)計算整個證書的hash值(指紋)并和證書放在一起,使用者在打開證書時,自己也根據(jù)指紋算法計算一下證書的hash值(指紋),如果和剛開始的值對得上,就說明證書沒有被修改過,因為證書的內(nèi)容被修改后,根據(jù)證書的內(nèi)容計算的出的hash值(指紋)是會變化的。 注意,這個指紋會使用"SecureTrust CA"這個證書機構(gòu)的私鑰用簽名算法(Signature algorithm)加密后和證書放在一起。
證書頒發(fā)機構(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ā)機構(gòu): 設(shè)置 -> 安全 -> 受信任的憑證
最佳和最安全的私鑰獲取方法是向可信任的證書頒發(fā)機構(gòu) (CA)(例如將帶您完成身份驗證過程的賽門鐵克公司)申請一個證書。一旦有了自己的證書,就可以生成您自己的私鑰。您可以使用該私鑰為您的可執(zhí)行文件或軟件庫簽名,私鑰僅可通過可追溯到 CA 的公鑰解鎖,而公鑰已預(yù)安裝在大部分瀏覽器中。如果在簽名后代碼被篡改,公鑰將不能核實私鑰簽名的真實性,瀏覽器將立即向任何嘗試下載代碼的人彈出警告窗口。如果代碼完好無損,則將提供您的文。
什么是自簽名證書?
自簽名證書就是沒有通過受信任的證書頒發(fā)機構(gòu), 自己給自己頒發(fā)的證書.
SSL 證書大致分三類:
- 由安卓認可的證書頒發(fā)機構(gòu)(如: VeriSign), 或這些機構(gòu)的下屬機構(gòu)頒發(fā)的證書.
- 沒有得到安卓認可的證書頒發(fā)機構(gòu)頒發(fā)的證書.
- 自己頒發(fā)的證書, 分臨時性的(在開發(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,打開看是二進制格式,不可讀.
查看DER格式證書的信息:openssl x509 -in certificate.der -inform der -text -noout
Java和Windows服務(wù)器偏向于使用這種編碼格式.
KeyStore
在 Android 和 java中 更多的是使用keystore。最常用的工具就是keytool.
Keytool是一個Java數(shù)據(jù)證書的管理工具 ,Keytool將密鑰(key)和證書(certificates)存在一個稱為keystore的文件中。
在keystore里,包含兩種數(shù)據(jù):
- 密鑰實體(KeyEntry)——密鑰(secret key)
- 可信任的證書實體(CertEntry)——只包含公鑰
每個keystore都關(guān)聯(lián)這一個獨一無二的alias,這個alias通常不區(qū)分大小寫
Android 中如何配置SSL/TLS
以上所述皆是對HTTPS SSL/TLS的概念敘述,通過這個描述會讓你對下面的配置以及編碼更加了解。我們在Android 開發(fā)中配置項目支持HTTPS這是個基本的操作。下面我會以O(shè)kHttp網(wǎng)絡(luò)框架為例。通過以下三點可完成項目配置。
- 如何配置系統(tǒng)、用戶信任證書校驗?
- 自簽名證書如何在項目中配置?
- 如何通過配置文件進行配置SSL/TLS證書?
如何配置系統(tǒng)、用戶信任證書校驗?
Android OkHttp框架默認是提供了對系統(tǒng)用戶信任證書的校驗。如果服務(wù)器下發(fā)的證書是被Android信任機構(gòu)頒發(fā)的證書是不會出現(xiàn)安全提示的,或者是用戶添加到系統(tǒng)的證書也是不會出現(xiàn)任何安全提示。
在 AOSP 源碼庫中,CA 根證書主要存放在 system/ca-certificates 目錄下,而在 Android 系統(tǒng)中,則存放在 /system/etc/security/ 目錄下。所以我們沒有Root的手機是無法進行添加根證書的。
也可以根據(jù)以下配置進行系統(tǒng)根證書安全校驗
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)威機構(gòu)頒發(fā)的證書還是自簽名的,打包一份到 app 內(nèi)部,比如存放在 asset 里,下面的示例展示了此過程:從 InputStream 獲取一個特定的 CA,用該 CA 創(chuàng)建 KeyStore,然后用后者創(chuàng)建和初始化 TrustManager。TrustManager 是系統(tǒng)用于驗證來自服務(wù)器的證書的工具,可以使用一個或多個 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);
以上代碼,可以解決證書信任問題。但同時需要注意的是,這里是基于Android默認的信任檢查來解決的。因為我們沒有對TrustManager做任何修改,如果仍然遇到證書校驗不通過的情況,則需要重新實現(xiàn)TrustManager,請用以下代碼代替“tmf.getTrustManagers()”:
。這時候我們需要通過自定義的校驗方式方可完成校驗。通過自定義X509TrustManager來實現(xiàn)checkServerTrusted的校驗,其實就是通過上訴介紹的SSL/TLS校驗的方式
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ù)置證書的公鑰對服務(wù)器下發(fā)的數(shù)字證書進行對比校驗。
如何通過文件配置證書?
Android P新特性,全面禁止了非安全的http連接,如果要使用非加密連接,需要配置network security config
步驟如下:
- 在res/xml下建立我們自己的network security config文件,名字任意,可以叫做network_security_config.xml
- 如果我們相對某些網(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 證書的主機 配置-->
<!-- cleartextTrafficPermitted="false" 強制校驗https 否則可以允許通過http-->
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">example.com</domain> <!-- 過濾域名,可配置多個 -->
<domain includeSubdomains="true">example1.com</domain> <!-- 一般會將 CDN 配置在此 -->
<trust-anchors>
<!--這就是我們自己的證書-->
<!--如果要信任多個證書,就可以寫多個-->
<certificates src="@raw/cn_area"/>
<!--也可以把這些證書放在一個目錄下-->
<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>
下面方式可以通過具體的需求進行具體的配置
如果我們的某個網(wǎng)址的證書是自簽名的證書,我們想要訪問這個網(wǎng)址,可以進行如下配置
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">example.com</domain>
<trust-anchors>
<!--這就是我們自己的證書-->
<!--如果要信任多個證書,就可以寫多個-->
<certificates src="@raw/my_ca"/>
<!--也可以把這些證書放在一個目錄下-->
<certificates src="@raw/trusted_roots"/>
</trust-anchors>
</domain-config>
</network-security-config>
如果我們想讓App信任除系統(tǒng)之外的其他的CA,可以進行如下配置
<?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>
如果我們在調(diào)試的時候staging服務(wù)器不是正規(guī)CA,我們可以進行如下配置
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!--debug-overrides表示在調(diào)試狀態(tài)下信任的CA,當(dāng)android:debugable為true的時候,是調(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)險。有些應(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>
更詳細的信息請參考Android Security Config
數(shù)字證書轉(zhuǎn)化https://blog.csdn.net/xiangguiwang/article/details/76400805