Android Https技術(shù)探索

最近在Https方面做了一些探索,在Android上做了一些實際應(yīng)用,在此分享出來以便后查。

Https協(xié)議是什么

目前,Https已經(jīng)成為了主流趨勢,無論在大型互聯(lián)網(wǎng)公司如BAT,還是小型創(chuàng)業(yè)公司,都逐漸將自己的服務(wù)切換成了Https。Https能提供一種安全可靠的加密通信連接方式,有效防止信息泄密以及劫持等情況發(fā)生。因此,使用Https無論從用戶體驗還是企業(yè)長期利益來看,都是十分有益的。

Http協(xié)議

我們首先說說什么是Http協(xié)議?Http是一個屬于應(yīng)用層的面向?qū)ο蟮膮f(xié)議,由于其簡捷、快速的方式,適用于分布式超媒體信息系統(tǒng)。

Http(超文本傳輸協(xié)議)是一個基于請求與響應(yīng)模式的、無狀態(tài)的、應(yīng)用層的協(xié)議,?;赥CP的連接方式,HTTP1.1版本中給出一種持續(xù)連接的機制,絕大多數(shù)的Web開發(fā),都是構(gòu)建在HTTP協(xié)議之上的Web應(yīng)用。

順便說一句,Http1.1協(xié)議已經(jīng)是目前最主流的協(xié)議,但是新的Http2.0協(xié)議也已經(jīng)提出,在未來10年的時間內(nèi),必將逐漸登上主舞臺。這種在應(yīng)用層的推廣會比IPV6這種網(wǎng)絡(luò)層的推廣快很多。

Http URL(URL是一種特殊類型的URI,包含了用于查找某個資源的足夠的信息)的格式如下:

http://host[":"port][abs_path]

Http的請求由三部分組成,分別是:請求行、消息報頭、請求正文。具體內(nèi)容可以查看WikiPedia,再次不多贅述。

SSL/TLS協(xié)議

那么什么是Https協(xié)議呢?簡單來說,Https = Http + SSL/TLS,即基于SSL的Http協(xié)議,使用不同于Http協(xié)議的默認端口及一個加密、身份驗證層(Http與TCP之間)。

Https實際上應(yīng)用了Netscape的安全全套接字層(SSL,TLS則是SSL的升級版,修復(fù)了SSL的漏洞)作為Http應(yīng)用層的子層。Https使用端口443,而不是像Http那樣使用端口80和TCP/IP進行通信。SSL使用40位關(guān)鍵字作為RC4流加密算法,這對于商業(yè)信息的加密是合適的。Https和SSL支持使用X.509數(shù)字認證,如果需要的話用戶可以確認發(fā)送者是誰。Https協(xié)議使用SSL在發(fā)送方把原始數(shù)據(jù)進行加密,然后在接受方進行解密,加密和解密需要發(fā)送方和接受方通過交換共知的密鑰來實現(xiàn),因此,所傳送的數(shù)據(jù)不容易被網(wǎng)絡(luò)黑客截獲和解密。

客戶端在使用Https方式與Web服務(wù)器通信時有以下幾個步驟:

  1. 客戶使用Https的URL訪問Web服務(wù)器,要求與Web服務(wù)器建立SSL連接。
  2. Web服務(wù)器收到客戶端請求后,會將網(wǎng)站的證書信息(證書中包含公鑰)傳送一份給客戶端。
  3. 客戶端的瀏覽器與Web服務(wù)器開始協(xié)商SSL連接的安全等級,也就是信息加密的等級。
  4. 客戶端的瀏覽器根據(jù)雙方同意的安全等級,建立會話密鑰,然后利用網(wǎng)站的公鑰將會話密鑰加密,并傳送給網(wǎng)站。
  5. Web服務(wù)器利用自己的私鑰解密出會話密鑰。
  6. Web服務(wù)器利用會話密鑰加密與客戶端之間的通信。

基本流程圖如下:

Https.png

另外,假如為了安全保密,將一個網(wǎng)站所有的Web應(yīng)用都啟用SSL技術(shù)來加密,并使用Https協(xié)議進行傳輸,那么該網(wǎng)站的性能和效率會降低,而且沒有這個必要,因為一般來說并不是所有數(shù)據(jù)都要求那么高的安全保密級別。我們只需對那些涉及機密數(shù)據(jù)的交互處理使用Https協(xié)議,這樣就做到魚與熊掌兼得。

Https在Android的使用——HttpsURLConnection

在Android領(lǐng)域,如何建立一個可靠的Https協(xié)議鏈接呢?Android是基于Java進行編程的,常見的Http協(xié)議建立方式有Apache的HttpClient和Sun公司提供的HttpURLConnection。不過,從API19開始,Google官方已經(jīng)推薦Android開發(fā)者使用HttpURLConnection來進行網(wǎng)絡(luò)連接,當然,為了更方便的使用Http,很多開源庫也是提供了許多方便的功能,比如Volley、OKHttp等。

但是,萬變不離其宗,基本流程都與HttpsURLConnection連接相似。這里我就主要分享一下關(guān)于HttpsURLConnection的使用心得。

首先,明確其繼承關(guān)系。

HttpsURLConnection -> HttpURLConnection -> URLConnection

從這個繼承關(guān)系可以看出Https其實就是Http的延伸,同時也是一種URLConnection的實現(xiàn)。

URLConnection是abstract類型,它是所有端到URL連接的一個父類,其實例可以用來讀寫URL的資源。要建立一個這樣的URL連接需要幾個步驟:

  1. openConnection:建立對應(yīng)URL的連接
  2. 設(shè)置參數(shù)和請求屬性
  3. connection:發(fā)起實質(zhì)的鏈接
  4. 獲取請求頭信息和請求內(nèi)容

其實,HttpURLConnection建立連接也就是遵循這一流程。下面給出一個基本的Http連接建立的實例:

URL url = new URL("http://www.baidu.com/");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
    InputStream in = new BufferedInputStream(urlConnection.getInputStream());
    readStream(in);
} finally {
    urlConnection.disconnect();
}

總結(jié)一下,基本是這幾個步驟:

  1. 通過調(diào)用URL.openConnection()獲取一個HttpURLConnection實例
  2. 設(shè)置相應(yīng)請求頭信息
  3. 設(shè)置上傳的請求體(這是可選項)setDoOutput(true)
  4. 讀取返回信息,消息頭包括消息體類型、長度、Cookie等,用getInputStream()獲取消息體
  5. 讀完信息后,斷開連接disconnect()

熟悉了HttpsURLConnection的父類,那么Https的連接就很好建立了。從前面的基礎(chǔ)知識,我們可以知道,在Http的基礎(chǔ)上增加TLS/SSL的校驗,就可以得到Https了。

URL.openConnection()打開一個協(xié)議為Https的Url,并返回一個HttpsURLConnection的實例,其余的設(shè)置與Http一模一樣,其實就可以建立一個默認的Https連接了。這是因為在Android默認的機制中已經(jīng)幫我們處理好了HostNameVarifySSLSocketFactory這兩個類,并且很友好地允許開發(fā)者重寫這兩個函數(shù),配置符合自己業(yè)務(wù)特點的校驗機制(后面會有具體應(yīng)用)。

另外,Android默認使用了X509TrustManager來解析證書鏈,這個是標準權(quán)威機構(gòu)的證書管理工具。在HTTPS的證書未經(jīng)權(quán)威機構(gòu)認證的情況下,也要訪問HTTPS站點的兩種方法,一種方法是把該證書導(dǎo)入到Java的TrustStore文件中,另一種是自己實現(xiàn)并覆蓋缺省的X509證書信任管理器類。

HttpDNS在Https的解決方案

什么是HttpDNS?HttpDNS使用Http協(xié)議進行域名解析,代替現(xiàn)有基于UDP的DNS協(xié)議,域名解析請求直接發(fā)送到HttpDNS服務(wù)器,從而繞過運營商的Local DNS,能夠避免Local DNS造成的域名劫持問題和調(diào)度不精準問題。目前,比較有名的就是阿里云的HttpDNS解析服務(wù)了,同時,其它各大公司也有對應(yīng)的域名解析服務(wù)。但是,將HttpDNS應(yīng)用到HttpDNS時,就出現(xiàn)一些棘手的問題了。

問題背景是這樣的:用HttpDNS來進行Https的連接,客戶端需要驗證服務(wù)端下發(fā)的證書,驗證過程有兩個關(guān)鍵:

  1. 本地保存的根證書解開服務(wù)器發(fā)送的證書鏈,確認服務(wù)端下發(fā)的證書是由可信任的機構(gòu)頒發(fā)的。
  2. 客戶端需要檢查證書的domain域和擴展域,看是否包含本次請求的host,即發(fā)送證書的是不是我的請求方。

上述兩點都校驗通過,就證明當前的服務(wù)端是可信任的,否則就是不可信任,應(yīng)當中斷當前連接。

當客戶端使用HttpDNS解析域名時,請求URL中的host會被替換成HttpDNS解析出來的IP,所以在證書驗證的第2步,會出現(xiàn)domain不匹配的情況,導(dǎo)致SSL/TLS握手不成功。因此,針對“domain不匹配”問題,需要hook證書校驗過程中第2步,即HostNameVerify,將IP直接替換成原來的域名,再執(zhí)行證書驗證。

/*
 * 使用HttpDNS在https的情況下的Demo
 * 適用于Java和Android
 */
private void connectHttps() {
    try {
        String originalUrl = "https://m.baidu.com/";
        URL url = new URL(originalUrl);
        connection = (HttpsURLConnection) url.openConnection();
        // 獲取IP Host地址
        String ip = getIpHost(url.getHost());
        if (ip != null) {
            // 進行URL替換和HOST頭設(shè)置
            String newUrl = originalUrl.replaceFirst(url.getHost(), ip);
            connection = (HttpsURLConnection) new URL(newUrl).openConnection();
            // 設(shè)置HTTP請求頭Host域
            connection.setRequestProperty("Host", url.getHost());
        }
        final String urlHost = connection.getURL().getHost();
        connection.setHostnameVerifier(new HostnameVerifier() {
            // 為解決HTTPDNS中,session攜帶的Host和IP切換后的Host不一致導(dǎo)致握手失敗
            @Override
            public boolean verify(String hostname, SSLSession session) {
                String host = connection.getRequestProperty("Host");
                boolean isHostNameVerify = HttpsURLConnection.getDefaultHostnameVerifier().verify(host, session);
                if (isHostNameVerify) {
                    // 判斷IP來源和Session的IP一致后,強制將Host信息賦值到Session中
                    if (!host.equals(session.getPeerHost()) && !urlHost.equals(session.getPeerHost())) {
                        return false;
                    }
                }
                return isHostNameVerify;
            }
        });
        DataInputStream dis = new DataInputStream(connection.getInputStream());
        int len;
        byte[] buff = new byte[4096];
        StringBuilder response = new StringBuilder();
        while ((len = dis.read(buff)) != -1) {
            response.append(new String(buff, 0, len));
        }
        Log.d(TAG, "Response: " + response.toString());
    } catch (Exception e) {
        e.printStackTrace();
    } finally {

    }
}

只有這樣處理,才能真正使用上HttpDNS服務(wù),相關(guān)介紹也可以去阿里云服務(wù)的官網(wǎng)上進行查看。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評論 19 139
  • 原文地址 http://blog.csdn.net/u012409247/article/details/4985...
    0fbf551ff6fb閱讀 3,688評論 0 13
  • 本文摘自 騰訊bugly 的文章《全站 HTTPS 來了》,內(nèi)容有修改。 大家在使用百度、谷歌或淘寶的時候,是否注...
    bnotes閱讀 3,875評論 1 9
  • 蒲公英很別致,很神奇,這是從小到大一個固有的認知。 每每在田間地頭看見它,就如同在河蚌中發(fā)現(xiàn)了珍珠一般,如獲至寶,...
    A于航閱讀 743評論 0 1
  • 深夜襲來,毫無睡意,想念的人遠在天邊,聽著窗外的聲聲犬吠和空調(diào)呼呼的風(fēng)聲,少有的失眠和頹廢,都在黑暗中匍匐,然后在...
    思南慕北閱讀 426評論 0 0

友情鏈接更多精彩內(nèi)容