https = http +ssl 即基于于HTTP協議,通過SSL或TLS提供加密處理數據、驗證對方身份以及數據完整性保護
## 優點
內容加密:采用混合加密技術,中間者無法直接查看明文內容
驗證身份:通過證書認證客戶端訪問的是自己的服務器
保護數據完整性:防止傳輸的內容被中間人冒充或者篡改、
## 加密類型
先科普一下,加密算法的類型基本上分為了兩種:
### 對稱加密
**指加密和解密使用相同的密鑰**
典型的有DES、RC5、IDEA(分組加密),RC4(序列加密);
### 非對稱加密
**又稱為公鑰加密算法**,是指加密和解密使用不同的密鑰(公開的公鑰用于加密,私有的私鑰用于解密
比如A發送,B接收,A想確保消息只有B看到,需要B生成一對公私鑰,并拿到B的公鑰。于是A用這個公鑰加密消息,B收到密文后用自己的與之匹配的私鑰解密即可。
反過來也可以用私鑰加密公鑰解密。也就是說對于給定的公鑰有且只有與之匹配的私鑰可以解密,對于給定的私鑰,有且只有與之匹配的公鑰可以解密
典型的算法有RSA,DSA,DH;
**由于非對稱加密,有公鑰也沒有解密,也因此,相比較對稱加密而言,非對稱加密安全性更高,但是加解密耗費的時間更長,速度慢。**
### 散列算法
散列變換是指把文件內容通過某種公開的算法,變成固定長度的值(散列值),這個過程可以使用密鑰也可以不使用。
這種散列變換是不可逆的,也就是說不能從散列值變成原文。因此,散列變換通常用于驗證原文是否被篡改。
典型的算法有:MD5,SHA,Base64,CRC等
## CA 證書
### 關于證書:
服務端向客戶端下發自己的證書,通常是CA認證的證書。證書包括了很多信息,主要有“公鑰信息”、“簽名”、“組織機構地區等信息”、“證書頒發機構”,關聯的中級證書(medium certificate)、根證書(root certificate)等。
X.509 應該是比較流行的 SSL 數字證書標準,包含(但不限于)以下的字段:
| 字段 | 值說明 |
| --- | --- |
| 對象名稱(Subject Name) | 用于識別該數字證書的信息 |
| 共有名稱(Common Name) | 對于客戶證書,通常是相應的域名 |
| 證書頒發者(Issuer Name) | 發布并簽署該證書的實體的信息 |
| 簽名算法(Signature Algorithm) | 簽名所使用的算法 |
| 序列號(Serial Number) | 數字證書機構(Certificate Authority, CA)給證書的唯一整數,一個數字證書一個序列號 |
| 生效期(Not Valid Before) | (`?ω?′) |
| 失效期(Not Valid After) | (╯°口°)╯(┴—┴ |
| 公鑰(Public Key) | 可公開的密鑰 |
| 簽名(Signature) | 通過簽名算法計算證書內容后得到的數據,用于驗證證書是否被篡改 |
**主要就是簽名,用于驗證是否篡改過**
### 客戶端如何通過證書確定服務端的身份?
證明下面兩點,(然后才可以使用證書上的公鑰來加密生成Session key的隨機數)
> 1. 證明baidu.com這個證書確實是百度的
> 2. 證明baidu.com這個證書沒有被其他人攥改過
證書以證書鏈的形式組織,在頒發證書的時候首先要有根CA機構頒發的根證書,再由根CA機構頒發一個中級CA機構的證書,最后由中級CA機構頒發具體的SSL證書。
數字證書采用信任鏈驗證。數字證書的信任錨(信任的起點)就是根證書頒發機構。根證書(root certificate)是一個無簽名或自簽名的證書。是用于識別根證書頒發機構(CA)的公鑰證書。
驗證的具體實現如下圖所示: 
1. 從左往右,用戶證書指向簽署它的中級證書,并且,用戶證書的摘要經由中級證書的私鑰加密,密文作為該用戶的證書簽名(signature)記錄在用戶證書上。
2. 中級證書相應的,指向簽署它的根證書,同時,中級證書的摘要經由根證書的私鑰加密,密文作為中級證書的簽名記錄在中級證書上
3. 根證書在簽署的時候,使用簽發機構的私鑰對證書的摘要進行加密,密文作為根證書的簽名記錄在根證書上。
**如何驗證證書**
#### 1.如何驗證證書沒有被篡改過
比如驗證用戶證書A,需要用到中級證書的公鑰B解密前者的簽名得到摘要 Digest1,我們的客戶端也計算A證書的內容得到摘要 Digest2。對比這兩個摘要就能知道前者是否被篡改。后者同理,使用他的證書簽發者提供的公鑰驗證。當驗證到**到受信任的根證書**時,就能確定這個證書是可信的。
#### 2.為什么根證書是可信的
數字證書認證機構(Certificate Authority, CA)簽署和管理的 **CA 根證書**,會被納入到你的瀏覽器和操作系統的可信證書列表中,并由這個列表判斷根證書是否可信。所以不要隨便導入奇奇怪怪的根證書到你的操作系統中。
## HTTPS握手的過程
在 HTTPS 加密原理的過程中把對稱加密和非對稱加密都利用了起來,SSL協議是用非對稱密碼算法來協商密鑰,并使用密鑰加密明文并傳輸的
HTTPS大體分為以下幾個步驟:
1. 驗證證書的有效性(是否被更改,是否合法)
2. 握手生成會話密鑰
3. 利用會話密鑰進行內容傳輸
假設現在有客戶端 A 和服務器 B :
1. 首先,客戶端 A 訪問服務器 B ,比如我們用瀏覽器打開一個網頁https://www.baidu.com,這時,瀏覽器就是客戶端 A ,百度的服務器就是服務器 B 了。
2. 時候客戶端 A 會生成一個隨機數1,把**隨機數1 、自己支持的 SSL 版本號以及支持的加密**算法(譬如說,對稱加密算法有DES,RC5,密鑰交換算法有RSA和DH,摘要算法有MD5和SHA)等這些信息告訴服務器 B。
3. 服務器 B 知道這些信息后,然后確認一下雙方的加密算法,”用DES-RSA-SHA這對組合好了“,**然后服務端也生成一個隨機數 B ,并將隨機數 B 和 CA 頒發給自己的證書一同返回給客戶端 A。**
4. 客戶端 A 得到 CA 證書后,會去校驗該 CA 證書的有效性,校驗方法在上面已經說過了。校驗通過后,**客戶端生成一個隨機數3 ,然后用證書中的公鑰加密隨機數3 并傳輸給服務端 B 。**
5. 服務端 B 得到加密后的隨機數3,然后利用私鑰進行解密,得到真正的隨機數3。
6. 最后,客戶端 A 和服務端 B 都有**隨機數1、隨機數2、隨機數3**,然后雙方利用這三個隨機數生成一個對話密鑰。之后傳輸內容就是利用對話密鑰來進行加解密了。這時就是利用了對稱加密,一般用的都是 AES 算法,也就是上文約定好的協議。
7. 客戶端 A 通知服務端 B ,指明后面的通訊用對話密鑰來完成,同時通知服務器 B 客戶端 A 的握手過程結束。
8. 服務端 B 通知客戶端 A,指明后面的通訊用對話密鑰來完成,同時通知客戶端 A 服務器 B 的握手過程結束。
9. SSL 的握手部分結束,SSL 安全通道的數據通訊開始,客戶端 A 和服務器 B 開始使用相同的對話密鑰進行數據通訊。
到此,SSL 握手過程就講完了。可能上面的流程太過于復雜,我們簡單地來講:
1. 客戶端和服務端建立 SSL 握手,客戶端通過 CA 證書來確認服務端的身份;
2. 互相傳遞三個隨機數,之后通過這隨機數來生成一個密鑰;
3. 互相確認密鑰,然后握手結束;
4. 數據通訊開始,都使用同一個對話密鑰來加解密;
## 為什么一定要用三個隨機數,來生成"會話密鑰"?
答:不管是客戶端還是服務器,都是下需要隨機數,這樣生成的密鑰才不會每次都一樣,由于SSL協議中證書是靜態的,因此十分有必要引入一種隨機因素來保證協商出的密鑰的隨機性。
對于RSA密鑰交換算法來說,pre-master-key本身就是一個隨機數,再加上第一步、第三步消息中的隨機數,三個隨機數通過一個密鑰導出器最終導出一個對稱密鑰。pre master 的存在在于SSL協議不信任每一個主機都能產生完全的隨機數,如果隨機數不隨機,那么pre master secret就可能被猜出來,那么僅適用于pre master secret作為密鑰就不合適了,因此必須引入新的隨機因素,那么客戶端和服務器加上pre master secret三個隨機數一同生成的密鑰就不容易被猜出了,一個偽隨機數可能完全不隨機,但是三個偽隨機就十分接近隨機了,每增加一個自由度,隨機性增加的可不是一。
### 中間人攻擊與https抓包
一個針對SSL的中間人攻擊過程如下:

中間人其實是做了一個偷梁換柱的動作,核心是如何欺騙客戶端,從而讓客戶端能夠放心的與中間人進行數據交互而沒有任何察覺。我們來看Charles如何做到HTTPS抓包的,網上有很多fiddlers如何抓HTTPS包的教程,幾步就搞定了,其中最核心的就是:
**將私有CA簽發的數字證書安裝到手機中并且作為受信任證書保存**
當私有的CA證書添加到系統信任證書后,就可以完成證書鏈驗證過程
fiddler抓包過程,詳情可見:[www.cnblogs.com/afeng2010/p…](https://link.juejin.cn?target=https%3A%2F%2Fwww.cnblogs.com%2Fafeng2010%2Fp%2F10073446.html "https://www.cnblogs.com/afeng2010/p/10073446.html")
#### android7.0之后用戶CA限制
Android從7.0開始系統不再信任用戶CA證書(應用targetSdkVersion >= 24時生效,如果targetSdkVersion = 24的應用的HTTPS包就抓不到了。
Android 6.0(API 23)及更低版本應用的默認網絡安全性配置如下:
~~~java
<!-- 默認允許所有明文通信 -->
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<!-- 信任系統預裝 CA 證書 -->
<certificates src="system" />
<!-- 信任用戶添加的 CA 證書,Charles 和 Fiddler 抓包工具安裝的證書屬于此類 -->
<certificates src="user" />
</trust-anchors>
</base-config>
~~~
而在 Android 7.0(API 24)到 Android 8.1(API 27)的默認網絡安全性配置如下:
~~~java
<!-- 默認允許所有明文通信 -->
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<!-- 信任系統預裝 CA 證書 -->
<certificates src="system" />
</trust-anchors>
</base-config>
~~~
而在 Android 9.0(API 28)及更高版本的默認網絡安全性配置如下:
~~~java
<!-- 默認禁止所有明文通信 -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<!-- 信任系統預裝 CA 證書 -->
<certificates src="system" />
</trust-anchors>
</base-config>
復制代碼
~~~
如果我們要抓自已APP的包,解決方式就是使用 Android 6.0 以下的網絡安全性配置: 添加res/xml/network\_security\_config.xml:
~~~java
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
~~~
然后在清單文件中指向該文件:
~~~java
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>
</manifest>
~~~
### 如何防止中間人攻擊
通過上面的講解,大家可以知道
假設你的設備沒有安裝信任過來歷不明的證書,那么不管在任何WIFI或者網絡環境下,通常HTTPS通信都是安全的
并且在android7.0及以上,就算手機安裝信任了證書,在一般情況下也是安全的,因為android7.0以上默認不信任用戶證書
所以通常情況下,我們不需要做什么特別操作來防止中間人攻擊
不過有部分金融類的APP需要額外校驗來保證https的安全性
我們可以下載服務器端公鑰證書,然后將公鑰證書編譯到Android應用中一般在assets文件夾保存,由應用在交互過程中去驗證證書的合法性。
~~~java
//使用內置證書
public static SSLSocketFactory getSSLSocketFactory_Certificate(Context context) {
try {
//獲取X.509格式的內置證書
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
Collection<? extends Certificate> cers = certificateFactory.generateCertificates(context.getAssets().open("CACertificate.cer"));
int index = 0;
for (Certificate certificate : cers) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificate);
}
//用這個KeyStore去引導生成的TrustManager來提供驗證
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
//用TrustManager生成SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
//SSLContext返回SSLSocketFactory
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
~~~
## 參考資料
[https加密原理](https://blog.csdn.net/fei20121106/article/details/84104843)
[Android程序員需要了解的https與中間人攻擊](https://juejin.cn/post/6880024440143347719)
- Android
- 四大組件
- Activity
- Fragment
- Service
- 序列化
- Handler
- Hander介紹
- MessageQueue詳細
- 啟動流程
- 系統啟動流程
- 應用啟動流程
- Activity啟動流程
- View
- view繪制
- view事件傳遞
- choreographer
- LayoutInflater
- UI渲染概念
- Binder
- Binder原理
- Binder最大數據
- Binder小結
- Android組件
- ListView原理
- RecyclerView原理
- SharePreferences
- AsyncTask
- Sqlite
- SQLCipher加密
- 遷移與修復
- Sqlite內核
- Sqlite優化v2
- sqlite索引
- sqlite之wal
- sqlite之鎖機制
- 網絡
- 基礎
- TCP
- HTTP
- HTTP1.1
- HTTP2.0
- HTTPS
- HTTP3.0
- HTTP進化圖
- HTTP小結
- 實踐
- 網絡優化
- Json
- ProtoBuffer
- 斷點續傳
- 性能
- 卡頓
- 卡頓監控
- ANR
- ANR監控
- 內存
- 內存問題與優化
- 圖片內存優化
- 線下內存監控
- 線上內存監控
- 啟動優化
- 死鎖監控
- 崩潰監控
- 包體積優化
- UI渲染優化
- UI常規優化
- I/O監控
- 電量監控
- 第三方框架
- 網絡框架
- Volley
- Okhttp
- 網絡框架n問
- OkHttp原理N問
- 設計模式
- EventBus
- Rxjava
- 圖片
- ImageWoker
- Gilde的優化
- APT
- 依賴注入
- APT
- ARouter
- ButterKnife
- MMKV
- Jetpack
- 協程
- MVI
- Startup
- DataBinder
- 黑科技
- hook
- 運行期Java-hook技術
- 編譯期hook
- ASM
- Transform增量編譯
- 運行期Native-hook技術
- 熱修復
- 插件化
- AAB
- Shadow
- 虛擬機
- 其他
- UI自動化
- JavaParser
- Android Line
- 編譯
- 疑難雜癥
- Android11滑動異常
- 方案
- 工業化
- 模塊化
- 隱私合規
- 動態化
- 項目管理
- 業務啟動優化
- 業務架構設計
- 性能優化case
- 性能優化-排查思路
- 性能優化-現有方案
- 登錄
- 搜索
- C++
- NDK入門
- 跨平臺
- H5
- Flutter
- Flutter 性能優化
- 數據跨平臺