說道HTTPS,不得不提HTTP,HTTP最大的缺陷就是明文傳輸,數據傳輸過程當中很容易被篡改,因此美國網景公司提出來HTTPS協議,相對HTTP,HTTPS多了一個S,這個S,其實就是SSL/TSL,SSL全稱安全套接字層,TSL1.0(傳輸層安全協議)是SSL3.0的升級版,是用於服務器和客戶端加密通訊的,因此能夠認爲二者是同一種協議,SSL由於自身的不安全性,在Android8.0已經被棄用了,以上能夠看出HTTPS=HTTP+SSL/TLSjava
1.HTTPS發起SSL鏈接,連接到服務器的443端口算法
2.服務器向客戶端發送公鑰和數字證書spring
3.客戶端經過隨機算法生成私鑰,而後經過服務器公鑰對該私鑰加密,生成對稱密鑰瀏覽器
4.客戶端向服務器發送對稱密鑰安全
5.服務器經過對稱密鑰對數據進行加密bash
6.客戶端經過對稱密鑰來對數據解密服務器
那問題來了,在第二步,若是有好事者截獲了服務器對客戶端發送的公鑰,而後僞形成服務器與客戶端通訊,這可如何是好呢,如何判斷該公鑰是合法的呢,數字證書就排上用場了網絡
數字證書是由CA簽發,全世界權威的CA一共100多個,數字證書裏包含一對非對稱密鑰,公鑰和私鑰以及頒發給、頒發者等信息,裏面的公鑰對服務器端傳輸的公鑰進行加密,生成密文,而後由客戶端的數字證書裏的私鑰進行解密,從而得到服務器的公鑰並確認該公鑰是合法的工具
以瀏覽器的www.baidu.com爲例,經過點擊連接旁的鎖標誌來打開數字證書ui
證書路徑展現的是證書鏈,根證書的權利最大,依次爲根證書>A>B>C,若是www.baidu.com 信任了根證書,則意味着A、B、C都信任
經過詳細信息能夠看到公鑰、公鑰參數、證書策略等證書的詳細信息 手機做爲訪問網絡的客戶端,固然也會有內置證書,以小米手機爲例,經過設置-更多設置-系統安全-加密與憑據-信任的憑據來打開內置的CA證書
系統證書是內置的, 我的證書是自定義的,好比charles的抓包證書是放在 我的證書下面的,那Android是如何發起HTTPS請求的呢?日常使用的抓包工具,不管是fidder和charles都能解析客戶端和服務器的HTTPS數據,是如何作到的呢?其實抓包工具就充當了一箇中間人代理的角色,參照2.https的工具原理,抓包的工做原理以下:
Android中如何訪問HTTPS呢,其實Retrofit、OkHttp均支持HTTPS的訪問 項目中引入網絡庫,以**implementation 'com.squareup.okhttp3:okhttp:4.2.0'**爲例,
final OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
final Request request = new Request.Builder()
.url("https://www.baidu.com/robots.txt")
.build();
final Response execute = okHttpClient.newCall(request).execute();
final String bodyStr = execute.body().string();
Log.d(TAG, bodyStr);
複製代碼
那若是關閉客戶端的CA證書,GlobalSign Root CA-R1,至關於不信任百度服務器的數字證書,會致使報錯
Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
經過手動將GlobalSign Root CA-R1.cer
放入項目中的assets文件夾,則可避免這一錯誤,如何引用項目中集成的證書呢? 經過
SSLContext sslContext;
try {
InputStream inputStream = getAssets().open("");
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{OkhttpU.trustManagerForCertificates(inputStream)}, null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, OkhttpU.trustManagerForCertificates(inputStream)).build();
final Request request = new Request.Builder()
.url("https://www.baidu.com/robots.txt")
.build();
final Response execute = okHttpClient.newCall(request).execute();
final String bodyStr = execute.body().string();
Log.d(TAG, bodyStr);
} catch (Exception e) {
e.printStackTrace();
}
複製代碼
public class OkhttpU {
public static X509TrustManager trustManagerForCertificates(InputStream in) throws GeneralSecurityException {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
// Put the certificates a key store.
char[] password = "password".toCharArray(); // Any password will work.
KeyStore keyStore = newEmptyKeyStore(password);
int index = 0;
for (Certificate certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificate);
}
// Use it to build an X509 trust manager.
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
}
private static KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream in = null; // By convention, 'null' creates an empty key store.
keyStore.load(in, password);
return keyStore;
} catch (IOException e) {
throw new AssertionError(e);
}
}
}
複製代碼
就能夠正常訪問https了
綜上,經過引入自定義證書,而後給OkHttp設置sslSocketFactory能夠有效的防止抓包,可是.cer放到assets下很容易被反編譯,能夠經過jdk下的命令keytool -printcert -rfc -file srca.cer導出字符串,而後經過
OkHttpClientManager.getInstance()
.setCertificates(new Buffer()
.writeUtf8(CER_STRING) //CER_STRING是處處的string常量
.inputStream());
複製代碼