https是一個創建在密碼學基礎之上的一種安全通訊協議,準確來講是基於HTTP協議和SSL/TSL的組合,要想理解HTTPS必須瞭解密碼學的一些相關基礎概念。java
公鑰密碼體制分爲三個部分,公鑰,私鑰,加密解密算法,加密解密過程以下:android
在對稱加密算法中,加密使用的密鑰和解密使用的密鑰是相同的。也就是說,加密和解密都是使用的同一個密鑰。所以對稱加密算法要保證安全性的話,密鑰要作好保密,只能讓使用的人知道,不能對外公開。這個和上面的公鑰密碼體制有所不一樣,公鑰密碼體制中加密是用公鑰,解密使用私鑰,而對稱加密算法中,加密和解密都是使用同一個密鑰,不區分公鑰和私鑰。 密鑰,通常就是一個字符串或數字,在加密或者解密時傳遞給加密/解密算法。前面在公鑰密碼體制中說到的公鑰、私鑰就是密鑰,公鑰是加密使用的密鑰,私鑰是解密使用的密鑰。算法
RSA密碼體制是一種公鑰密碼體制,公鑰公開,私鑰保密,它的加密解密算法是公開的。 由公鑰加密的內容能夠而且只能由私鑰進行解密,而且由私鑰加密的內容能夠而且只能由公鑰進行解密。也就是說,RSA的這一對公鑰、私鑰均可以用來加密和解密,而且一方加密的內容能夠由而且只能由對方進行解密。瀏覽器
在非對稱加密算法中,加密使用的密鑰和解密使用的密鑰是不相同的。前面所說的公鑰密碼體制就是一種非對稱加密算法,他的公鑰和是私鑰是不能相同的,也就是說加密使用的密鑰和解密使用的密鑰不一樣,所以它是一個非對稱加密算法。安全
瞭解了以上的基礎概念以後咱們就能夠聊聊爲何安全了,這裏咱們就能夠籠統的講一下當咱們打開一個https連接的時候發生了什麼事情。bash
經過上訴簡單的描述,也可瞭解到https通訊是是經過各類加密算法及證書比對校驗完成的一個http通訊。服務器
ssl Secure Sockets Layer,如今應該叫"TLS",但因爲習慣問題,咱們仍是叫"SSL"比較多,ssl證書是數字證書的一種,數字證書有較多的內容項,裏面的內容比較多——Version、Serial number、Signature algorithm 等等,挑幾個重要的解釋一下。網絡
·Symantec (which bought VeriSign's SSL interests and owns Thawte and Geotrust) 38.1% 市場份額app
·Comodo SSL 29.1%框架
·Go Daddy 13.4%
·GlobalSign 10%
安卓系統中, 你能夠在下列路徑中找到證書頒發機構: 設置 -> 安全 -> 受信任的憑證
最佳和最安全的私鑰獲取方法是向可信任的證書頒發機構 (CA)(例如將帶您完成身份驗證過程的賽門鐵克公司)申請一個證書。一旦有了本身的證書,就能夠生成您本身的私鑰。您可使用該私鑰爲您的可執行文件或軟件庫簽名,私鑰僅可經過可追溯到 CA 的公鑰解鎖,而公鑰已預安裝在大部分瀏覽器中。若是在簽名後代碼被篡改,公鑰將不能覈實私鑰簽名的真實性,瀏覽器將當即向任未嘗試下載代碼的人彈出警告窗口。若是代碼無缺無損,則將提供您的文。
自簽名證書就是沒有經過受信任的證書頒發機構, 本身給本身頒發的證書.
SSL 證書大體分三類:
X.509 - 這是一種證書標準,主要定義了證書中應該包含哪些內容.其詳情能夠參考RFC5280,SSL/TLS使用的就是這種證書標準.
一樣的X.509證書,可能有不一樣的編碼格式,目前有如下兩種編碼格式.
PEM - Privacy Enhanced Mail,打開看文本格式,以"-----BEGIN..."開頭, "-----END..."結尾,內容是BASE64編碼. 查看PEM格式證書的信息:openssl x509 -in certificate.pem -text -noout Apache和*NIX服務器偏向於使用這種編碼格式.
DER - Distinguished Encoding Rules,打開看是二進制格式,不可讀. 查看DER格式證書的信息:openssl x509 -in certificate.der -inform der -text -noout Java和Windows服務器偏向於使用這種編碼格式.
在 Android 和 java中 更多的是使用keystore。最經常使用的工具就是keytool. Keytool是一個Java數據證書的管理工具 ,Keytool將密鑰(key)和證書(certificates)存在一個稱爲keystore的文件中。 在keystore裏,包含兩種數據:
每一個keystore都關聯這一個獨一無二的alias,這個alias一般不區分大小寫
以上所述皆是對HTTPS SSL/TLS的概念敘述,經過這個描述會讓你對下面的配置以及編碼更加了解。咱們在Android 開發中配置項目支持HTTPS這是個基本的操做。下面我會以OkHttp網絡框架爲例。經過如下三點可完成項目配置。
Android OkHttp框架默認是提供了對系統用戶信任證書的校驗。若是服務器下發的證書是被Android信任機構頒發的證書是不會出現安全提示的,或者是用戶添加到系統的證書也是不會出現任何安全提示。
在 AOSP 源碼庫中,CA 根證書主要存放在 system/ca-certificates 目錄下,而在 Android 系統中,則存放在 /system/etc/security/ 目錄下。因此咱們沒有Root的手機是沒法進行添加根證書的。
也能夠根據如下配置進行系統根證書安全校驗
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;
}
複製代碼
不管是權威機構頒發的證書仍是自簽名的,打包一份到 app 內部,好比存放在 asset 裏,下面的示例展現了此過程:從 InputStream 獲取一個特定的 CA,用該 CA 建立 KeyStore,而後用後者建立和初始化 TrustManager。TrustManager 是系統用於驗證來自服務器的證書的工具,可使用一個或多個 CA 從 KeyStore建立,而建立的 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作任何修改,若是仍然遇到證書校驗不經過的狀況,則須要從新實現TrustManager,請用如下代碼代替「tmf.getTrustManagers()」:
。這時候咱們須要經過自定義的校驗方式方可完成校驗。經過自定義X509TrustManager來實現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);
複製代碼
先校驗證書的日期是否過時,其次經過本地預置證書的公鑰對服務器下發的數字證書進行對比校驗。
Android P新特性,全面禁止了非安全的http鏈接,若是要使用非加密鏈接,須要配置network security config
步驟以下:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
//用於輔助抓包,7.0以上證書的權限變了
<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 模式配置調試CA-->
<debug-overrides>
<trust-anchors>
//信任用戶證書
<certificates src="user"></certificates>
//信任用戶本身安裝的證書
<certificates src="system"></certificates>
</trust-anchors>
</debug-overrides>
</network-security-config>
複製代碼
若是咱們的某個網址的證書是自簽名的證書,咱們想要訪問這個網址,能夠進行以下配置
<?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信任除系統以外的其餘的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>
複製代碼
若是咱們在調試的時候staging服務器不是正規CA,咱們能夠進行以下配置
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!--debug-overrides表示在調試狀態下信任的CA,當android:debugable爲true的時候,是調試狀態-->
<debug-overrides>
<trust-anchors>
<certificates src="@raw/debug_cas"/>
</trust-anchors>
</debug-overrides>
</network-security-config>
複製代碼
通常狀況下,應用信任全部預裝 CA。若是有預裝 CA 簽發欺詐性證書,則應用將面臨被中間人攻擊的風險。有些應用經過限制信任的 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
數字證書轉化https://blog.csdn.net/xiangguiwang/article/details/76400805