前言
大家都知道https相比http增加的是安全性。 怎麼增加安全性呢? 就是加密和解密步驟。 下面來詳細談談對https的理解和在Android中的使用.
兩種加密
加密方式分兩種,對稱加密和非對稱加密。這兩種方式都有自己的優劣勢, https中這兩種方式都採用了。 我們約定S是服務端,C是客戶端,客戶端需要從服務端獲取資訊;
對稱加密
這種加密方式比較簡單,就是雙方都持有密匙。S和C都持有密匙, S通過密匙加密明文傳遞給C,C獲取加密後的資訊,用密匙解密資訊。優勢: 加密速度快, 劣勢: 密匙的傳遞是個問題,容易被擷取,密匙一旦被擷取後, 就能輕易破解資訊。常見的對稱加密演算法有DES、3DES、TDEA、Blowfish、RC5和IDEA。
非對稱加密
非對稱加密中,S和C端都有自己的公鑰和私鑰。公鑰是公開的,私鑰是私有的,私鑰需要保密的。 這套公鑰和私鑰的有個兩種加密解密流程:
- 用公鑰加密的資訊,用私鑰才能解密。因為私鑰是私有的, 這種流程用於資訊的加密解密;
- 用私鑰加密資訊,用公鑰來解密。因為公鑰是共有的,這種流程用於認證。
在https中資訊傳遞的密匙的傳遞是採用非對稱加密傳遞的.
C端需要把資訊傳遞給S端, 需要分幾步.
- C端請求S端,S端把自己的公鑰傳遞給C端。
- C用S的公鑰把資訊加密後傳遞給S. S用自己的私鑰解密獲取資訊。 常用的非對稱加密演算法有RSA、Elgamal、Rabin、D-H、ECC(橢圓曲線加密演算法)等。
問:既然對稱加密和非對稱加密都需要保密好自己的私鑰, 那有什麼區別呢?
對稱加密中,私鑰不僅需要自己知道也需要解密方知道。 這樣私鑰就有一個傳遞的流程, 這個流程就會有很大風險。 而非對稱加密只需要自己保密好自己的私鑰就好了。 公鑰大家都知道,不需要保密,就少了一個私鑰傳遞的過程。 少了很大的風險。
HTTPS通訊過程
HTTPS中的SSL/TLS協議
HTTPS = HTTP + SSL/TLS協議
SSL的全稱是Secure Sockets Layer,即安全套接層協議,是為網路通訊提供安全及資料完整性的一種安全協議。SSL協議在1994年被Netscape發明,後來各個瀏覽器均支援SSL,其最新的版本是3.0; TLS的全稱是Transport Layer Security,即安全傳輸層協議,最新版本的TLS建立在SSL 3.0協議規範之上.在理解HTTPS時候,可以把SSL和TLS看做是同一個協議。
HTTPS加密方式
HTTPS為了兼顧安全與效率,同時使用了對稱加密和非對稱加密。 資料是被對稱加密傳輸的,對稱加密過程需要客戶端的一個金鑰,為了確保能把該金鑰安全傳輸到伺服器端; 採用非對稱加密對該金鑰進行加密傳輸,總的來說,對資料進行對稱加密,對稱加密所要使用的金鑰通過非對稱加密傳輸。
HTTPS通訊流程
一個HTTPS請求實際上包含了兩次HTTP傳輸,可以細分為8步。
第一次HTTP請求:
- 客戶端向伺服器發起HTTPS請求,連線到伺服器的443埠。
- 伺服器端有一個金鑰對,即公鑰和私鑰,是用來進行非對稱加密使用的,伺服器端儲存著私鑰,不能將其洩露,公鑰可以傳送給任何人。
- 伺服器將自己的公鑰傳送給客戶端。
- 客戶端收到伺服器端的公鑰之後,會對公鑰進行檢查,驗證其合法性,如果公鑰合格,那麼客戶端會生成一個隨機值,這個隨機值就是用於進行對稱加密的金鑰,我們將該金鑰稱之為client key,即客戶端金鑰,這樣在概念上和伺服器端的金鑰容易進行區分。然後用伺服器的公鑰對客戶端金鑰進行非對稱加密,這樣客戶端金鑰就變成密文了,至此,HTTPS中的第一次HTTP請求結束。
第二次HTTP請求:
- 客戶端會發起HTTPS中的第二個HTTP請求,將加密之後的客戶端金鑰傳送給伺服器。
- 伺服器接收到客戶端發來的密文之後,會用自己的私鑰對其進行非對稱解密,解密之後的明文就是客戶端金鑰,然後用客戶端金鑰對資料進行對稱加密,這樣資料就變成了密文。
- 然後伺服器將加密後的密文傳送給客戶端。
- 客戶端收到伺服器傳送來的密文,用客戶端金鑰對其進行對稱解密,得到伺服器傳送的資料。這樣HTTPS中的第二個HTTP請求結束,整個HTTPS傳輸完成。
數字證書
為什麼需要數字證書
在https中需要證書,證書的作用是為了防止"中間人攻擊"的。 如果有個中間人M攔截客戶端請求,然後M向客戶端提供自己的公鑰,M再向服務端請求公鑰,作為"中介者" 這樣客戶端和服務端都不知道,資訊已經被攔截獲取了。這時候就需要證明服務端的公鑰是正確的.
怎麼證明呢?
就需要權威第三方機構來公正了.這個第三方機構就是CA. 也就是說CA是專門對公鑰進行認證,進行擔保的,也就是專門給公鑰做擔保的擔保公司。 全球知名的CA也就100多個,這些CA都是全球都認可的,比如VeriSign、GlobalSign等,國內知名的CA有WoSign。
數字證書怎麼起作用
不論什麼平臺,裝置的作業系統中都會內建100多個全球公認的CA,說具體點就是裝置中儲存了這些知名CA的公鑰。當客戶端接收到伺服器的數字證書的時候,會進行如下驗證:
- 首先客戶端會用裝置中內建的CA的公鑰嘗試解密數字證書,如果所有內建的CA的公鑰都無法解密該數字證書,說明該數字證書不是由一個全球知名的CA簽發的,這樣客戶端就無法信任該伺服器的數字證書。
- 如果有一個CA的公鑰能夠成功解密該數字證書,說明該數字證書就是由該CA的私鑰簽發的,因為被私鑰加密的密文只能被與其成對的公鑰解密。
- 除此之外,還需要檢查客戶端當前訪問的伺服器的域名是與數字證書中提供的“頒發給”這一項吻合,還要檢查數字證書是否過期等。
證書鏈
一般CA不會直接去使用自己的私鑰去簽名某網站的證書, 一般CA會簽發一個子證書, 然後用這子證書去籤網站的證書. 有可能有多個子證書. 如果父證書是可以被信任的,那麼這個子證書就是可以被信任的.
Android中HTTPS的使用
在程式碼中配置https的證書的程式碼如下:
//配置:
setCertificates(builder, application.getAssets().open("xxxx.cer"));
/**
* 設定簽名證書
*
* @param builder
* @param certificates
*/
public void setCertificates(OkHttpClient.Builder builder, InputStream... certificates) {
try {
//建立X.509格式的CertificateFactory
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
// 建立一個預設型別的KeyStore,儲存我們信任的證書
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
//從asserts中獲取證書的流
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
//將證書ca作為信任的證書放入到keyStore中
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e) {
LogUtils.debugInfo("https證書錯誤1");
}
}
//建立TLS型別的SSLContext物件, that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
//TrustManagerFactory是用於生成TrustManager的,我們建立一個預設型別的TrustManagerFactory
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
//配置到OkHttpClient 或者
builder.sslSocketFactory(sslContext.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
LogUtils.debugInfo("https證書錯誤2");
}
}
複製程式碼
需要注意的是,如果自定義X509TrustManager的時候一定要複寫其中三個重要的方法, 如下錯誤的程式碼:
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
//do nothing,接受任意客戶端證書
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
//do nothing,接受任意服務端證書
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
複製程式碼
正確的做法:
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.
// ca是通過證書流獲取的證書
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];
}
}
}
複製程式碼
總結:
本文中分析總結了https的加密流程和在Android中的用法. 希望對大家有所幫助.