- https:在http(超文本傳輸協(xié)議)基礎(chǔ)上提出的一種安全的http協(xié)議,因此可以稱為安全的超文本傳輸協(xié)議。http協(xié)議直接放置在TCP協(xié)議之上,而https提出在http和TCP中間加上一層加密層。從發(fā)送端看,這一層負(fù)責(zé)把http的內(nèi)容加密后送到下層的TCP,從接收方看,這一層負(fù)責(zé)將TCP送來的數(shù)據(jù)解密還原成http的內(nèi)容。
- SSL(Secure Socket Layer):是Netscape公司設(shè)計的主要用于WEB的安全傳輸協(xié)議。在https協(xié)議棧中負(fù)責(zé)實現(xiàn)上面提到的加密層。
- 補(bǔ)充:IP(網(wǎng)絡(luò)層)、TCP(傳輸層),HTTP(應(yīng)用層),SSL處于TCP和HTTP之間
- 數(shù)字證書:一種文件的名稱,好比一個機(jī)構(gòu)或人的簽名,能夠證明這個機(jī)構(gòu)或人的真實性。其中包含的信息,用于實現(xiàn)上述功能。
- 加密和認(rèn)證:加密是指通信雙方為了防止敏感信息在信道上被第三方竊聽而泄漏,將明文通過加密變成密文,如果第三方無法解密的話,就算他獲得密文也無能為力;認(rèn)證是指通信雙方為了確認(rèn)對方是值得信任的消息發(fā)送或接受方,而不是使用假身份的騙子,采取的確認(rèn)身份的方式。只有同時進(jìn)行了加密和認(rèn)真才能保證通信的安全,因此在SSL通信協(xié)議中這兩者都很重要。
https依賴一種實現(xiàn)方式,目前通用的是SSL,數(shù)字證書是支持這種安全通信的文件。另外有SSL衍生出TLS和WTLS,前者是IEFT將SSL標(biāo)準(zhǔn)化之后產(chǎn)生的(TLS1.0),與SSL差別很小,后者是用于無線環(huán)境下的TSL。
圖解HTTPS協(xié)議加密解密全過程
我們都知道HTTPS能夠加密信息,以免敏感信息被第三方獲取。所以很多銀行網(wǎng)站或電子郵箱等等安全級別較高的服務(wù)都會采用HTTPS協(xié)議。
HTTPS其實是由兩部分組成:HTTP + SSL / TLS,也就是在HTTP上又加了一層處理加密信息的模塊。服務(wù)端和客戶端的信息傳輸都會通過TLS進(jìn)行加密,所以傳輸?shù)臄?shù)據(jù)都是加密后的數(shù)據(jù)。具體是如何進(jìn)行加密,解密,驗證的,且看下圖。
- 客戶端發(fā)起HTTPS請求
這個沒什么好說的,就是用戶在瀏覽器里輸入一個https網(wǎng)址,然后連接到server的443端口。 - 服務(wù)端的配置
采用HTTPS協(xié)議的服務(wù)器必須要有一套數(shù)字證書,可以自己制作,也可以向組織申請。區(qū)別就是自己頒發(fā)的證書需要客戶端驗證通過,才可以繼續(xù)訪問,而使用受信任的公司申請的證書則不會彈出提示頁面(startssl就是個不錯的選擇,有1年的免費服務(wù))。這套證書其實就是一對公鑰和私鑰。如果對公鑰和私鑰不太理解,可以想象成一把鑰匙和一個鎖頭,只是全世界只有你一個人有這把鑰匙,你可以把鎖頭給別人,別人可以用這個鎖把重要的東西鎖起來,然后發(fā)給你,因為只有你一個人有這把鑰匙,所以只有你才能看到被這把鎖鎖起來的東西。 - 傳送證書
這個證書其實就是公鑰,只是包含了很多信息,如證書的頒發(fā)機(jī)構(gòu),過期時間等等。 - 客戶端解析證書
這部分工作是有客戶端的TLS來完成的,首先會驗證公鑰是否有效,比如頒發(fā)機(jī)構(gòu),過期時間等等,如果發(fā)現(xiàn)異常,則會彈出一個警告框,提示證書存在問題。如果證書沒有問題,那么就生成一個隨機(jī)值。然后用證書對該隨機(jī)值進(jìn)行加密。就好像上面說的,把隨機(jī)值用鎖頭鎖起來,這樣除非有鑰匙,不然看不到被鎖住的內(nèi)容。 - 傳送加密信息
這部分傳送的是用證書加密后的隨機(jī)值,目的就是讓服務(wù)端得到這個隨機(jī)值,以后客戶端和服務(wù)端的通信就可以通過這個隨機(jī)值來進(jìn)行加密解密了。 - 服務(wù)端解密信息
服務(wù)端用私鑰解密后,得到了客戶端傳過來的隨機(jī)值(私鑰),然后把內(nèi)容通過該值進(jìn)行對稱加密。所謂對稱加密就是,將信息和私鑰通過某種算法混合在一起,這樣除非知道私鑰,不然無法獲取內(nèi)容,而正好客戶端和服務(wù)端都知道這個私鑰,所以只要加密算法夠彪悍,私鑰夠復(fù)雜,數(shù)據(jù)就夠安全。 - 傳輸加密后的信息
這部分信息是服務(wù)端用私鑰加密后的信息,可以在客戶端被還原。 - 客戶端解密信息
客戶端用之前生成的私鑰解密服務(wù)端傳過來的信息,于是獲取了解密后的內(nèi)容。整個過程第三方即使監(jiān)聽到了數(shù)據(jù),也束手無策。
不同Android版本針對于TLS協(xié)議的默認(rèn)配置圖如下:
從上圖可以得出如下結(jié)論:
- TLSv1.0從API 1+就被默認(rèn)打開
- TLSv1.1和TLSv1.2只有在API 20+ 才會被默認(rèn)打開
也就是說低于API 20+的版本是默認(rèn)關(guān)閉對TLSv1.1和TLSv1.2的支持,若要支持則必須自己打開
服務(wù)器配置已經(jīng)可以支持TLS1.0、TLS1.1、TLS1.2 , 客戶端就不需要做任何處理,Android4.x系統(tǒng)會使用TLS1.0, 而 Android 4.4及之上版本會選擇TLS 1.2
當(dāng)服務(wù)器只支持TLS1.2,那需要Android4.x打開對TLS1.1和TLS1.2的支持
網(wǎng)絡(luò)請求庫使用的是okhttp,在APP中可以這樣初始化OkHttpClient,這里通過在AppParams中配置isBypassAuthen,來判斷是否繞過認(rèn)證,也就是無條件信任所有HTTPS網(wǎng)站
這里只是 單向認(rèn)證 客戶端對服務(wù)端證書的單向認(rèn)證
private void initHttpsClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder() .connectTimeout(30000L, TimeUnit.MILLISECONDS)
.readTimeout(30000L, TimeUnit.MILLISECONDS)
.addInterceptor(new LoggerInterceptor("OkHttpClient"))
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
if(AppParams.isBypassAuthen){
HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
builder.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager);
}else{
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
try {
sslContext.init(null, null, null);
} catch (KeyManagementException e) {
e.printStackTrace();
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
SSLSocketFactory socketFactory = new Tls12SocketFactory(sslContext.getSocketFactory());
builder.sslSocketFactory(socketFactory,new HttpsUtils.UnSafeTrustManager());
}
OkHttpClient okHttpClient = builder
.build();
OkHttpUtils.initClient(okHttpClient);
}
public class HttpsUtils
{
public static class SSLParams
{
public SSLSocketFactory sSLSocketFactory;
public X509TrustManager trustManager;
}
public static SSLParams getSslSocketFactory(InputStream[] certificates, InputStream bksFile, String password)
{
SSLParams sslParams = new SSLParams();
try
{
TrustManager[] trustManagers = prepareTrustManager(certificates);
KeyManager[] keyManagers = prepareKeyManager(bksFile, password);
SSLContext sslContext = SSLContext.getInstance("TLS");
X509TrustManager trustManager = null;
if (trustManagers != null)
{
trustManager = new MyTrustManager(chooseTrustManager(trustManagers));
} else
{
trustManager = new UnSafeTrustManager();
}
sslContext.init(keyManagers, new TrustManager[]{trustManager},null);
sslParams.sSLSocketFactory = sslContext.getSocketFactory();
sslParams.trustManager = trustManager;
return sslParams;
} catch (NoSuchAlgorithmException e)
{
throw new AssertionError(e);
} catch (KeyManagementException e)
{
throw new AssertionError(e);
} catch (KeyStoreException e)
{
throw new AssertionError(e);
}
}
private class UnSafeHostnameVerifier implements HostnameVerifier
{
@Override
public boolean verify(String hostname, SSLSession session)
{
return true;
}
}
public static class UnSafeTrustManager implements X509TrustManager
{
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
}
@Override
public X509Certificate[] getAcceptedIssuers()
{
return new java.security.cert.X509Certificate[]{};
}
}
private static TrustManager[] prepareTrustManager(InputStream... certificates)
{
if (certificates == null || certificates.length <= 0) return null;
try
{
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates)
{
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try
{
if (certificate != null)
certificate.close();
} catch (IOException e)
{
}
}
TrustManagerFactory trustManagerFactory = null;
trustManagerFactory = TrustManagerFactory.
getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
return trustManagers;
} catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
} catch (CertificateException e)
{
e.printStackTrace();
} catch (KeyStoreException e)
{
e.printStackTrace();
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
private static KeyManager[] prepareKeyManager(InputStream bksFile, String password)
{
try
{
if (bksFile == null || password == null) return null;
KeyStore clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(bksFile, password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, password.toCharArray());
return keyManagerFactory.getKeyManagers();
} catch (KeyStoreException e)
{
e.printStackTrace();
} catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
} catch (UnrecoverableKeyException e)
{
e.printStackTrace();
} catch (CertificateException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers)
{
for (TrustManager trustManager : trustManagers)
{
if (trustManager instanceof X509TrustManager)
{
return (X509TrustManager) trustManager;
}
}
return null;
}
private static class MyTrustManager implements X509TrustManager
{
private X509TrustManager defaultTrustManager;
private X509TrustManager localTrustManager;
public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException
{
TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
var4.init((KeyStore) null);
defaultTrustManager = chooseTrustManager(var4.getTrustManagers());
this.localTrustManager = localTrustManager;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException
{
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException
{
try
{
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce)
{
localTrustManager.checkServerTrusted(chain, authType);
}
}
@Override
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[0];
}
}
}
自行實現(xiàn)SSLSocketFactory ,實現(xiàn)對TLSv1.1、TLSv1.2的支持
public class Tls12SocketFactory extends SSLSocketFactory {
private static final String[] TLS_SUPPORT_VERSION = {"TLSv1.1", "TLSv1.2"};
final SSLSocketFactory delegate;
public Tls12SocketFactory(SSLSocketFactory base) {
this.delegate = base;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return patch(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return patch(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket patch(Socket s) {
if (s instanceof SSLSocket) {
((SSLSocket) s).setEnabledProtocols(TLS_SUPPORT_VERSION);
}
return s;
}
}
先建立自定義的SSLSocketFactory,enableTLSOnSocket里面設(shè)置支持的 TLS 版本
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory delegate;
public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
delegate = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return enableTLSOnSocket(delegate.createSocket());
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
}
return socket;
}
}
建立OkHttpClient客戶端時,設(shè)置進(jìn)去
OkHttpClient client=new OkHttpClient();
try {
client = new OkHttpClient.Builder()
.sslSocketFactory(new TLSSocketFactory())
.build();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}