HTTPS的雙向驗證

最近在寫一個數(shù)據(jù)安全傳輸?shù)捻椖浚延龅降目右约霸趺唇鉀Q的,都記錄下來。

1.為什么要用HTTPS?

因為HTTP協(xié)議是沒有加密的明文傳輸協(xié)議,如果APP采用HTTP傳輸數(shù)據(jù),則會泄露傳輸內容,可能被中間人劫持,修改傳輸?shù)膬热荨榱吮Wo用戶的信息安全、保護自己的商業(yè)利益,減少攻擊面,我們需要保障通信信道的安全,采用開發(fā)方便的HTTPS是比較好的方式,比用私有協(xié)議要好,省時省力。

2.HTTPS通信原理

HTTPS是HTTP over SSL/TLS,HTTP是應用層協(xié)議,TCP是傳輸層協(xié)議,在應用層和傳輸層之間,增加了一個安全套接層SSL/TLS:

Paste_Image.png

SSL/TLS層負責客戶端和服務器之間的加解密算法協(xié)商、密鑰交換、通信連接的建立,安全連接的建立過程如下所示:

3.如何使用HTTPS

3.1數(shù)字證書、CA與HTTPS

信息安全的基礎依賴密碼學,密碼學涉及算法和密鑰,算法一般是公開的,而密鑰需要得到妥善的保護,密鑰如何產(chǎn)生、分配、使用和回收,這涉及公鑰基礎設施。

公鑰基礎設施(PKI)是一組由硬件、軟件、參與者、管理政策與流程組成的基礎架構,其目的在于創(chuàng)造、管理、分配、使用、存儲以及撤銷數(shù)字證書。公鑰存儲在數(shù)字證書中,標準的數(shù)字證書一般由可信數(shù)字證書認證機構(CA,根證書頒發(fā)機構)簽發(fā),此證書將用戶的身份跟公鑰鏈接在一起。CA必須保證其簽發(fā)的每個證書的用戶身份是唯一的。

鏈接關系(證書鏈)通過注冊和發(fā)布過程創(chuàng)建,取決于擔保級別,鏈接關系可能由CA的各種軟件或在人為監(jiān)督下完成。PKI的確定鏈接關系的這一角色稱為注冊管理中心(RA,也稱中級證書頒發(fā)機構或者中間機構)。RA確保公鑰和個人身份鏈接,可以防抵賴。如果沒有RA,CA的Root 證書遭到破壞或者泄露,由此CA頒發(fā)的其他證書就全部失去了安全性,所以現(xiàn)在主流的商業(yè)數(shù)字證書機構CA一般都是提供三級證書,Root 證書簽發(fā)中級RA證書,由RA證書簽發(fā)用戶使用的證書。

X509證書鏈,左邊的是CA根證書,中間的是RA中間機構,右邊的是用戶:

Paste_Image.png

3.2生成自己的CA根證書 , 生成服務端證書 省略

.............

3.3使用HttpsURLConnection進行HTTPS通信

獲取SSLContext

/**
     * 獲取SSLContext
     * @param context 上下文
     * @return SSLContext
     */
    private static SSLContext getSSLContext(Context context) {
        try {
            // 服務器端需要驗證的客戶端證書
            KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);
            // 客戶端信任的服務器端證書
            KeyStore trustStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);

            InputStream ksIn = context.getResources().getAssets().open(KEY_STORE_CLIENT_PATH);
            InputStream tsIn = context.getResources().getAssets().open(KEY_STORE_TRUST_PATH);
            try {
                keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray());
                trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    ksIn.close();
                } catch (Exception ignore) {
                }
                try {
                    tsIn.close();
                } catch (Exception ignore) {
                }
            }
            SSLContext sslContext = SSLContext.getInstance("TLS");
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(trustStore);
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
            keyManagerFactory.init(keyStore, KEY_STORE_PASSWORD.toCharArray());
            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
            return sslContext;
        } catch (Exception e) {
            Log.e("tag", e.getMessage(), e);
        }
        return null;
    }

獲取HttpsURLConnection

    /**
     * @param context 上下文
     * @param url     連接url
     * @param method  請求方式
     * @return HttpsURLConnection
     */
    public static HttpsURLConnection getHttpsURLConnection(Context context, String url, String method,
    String head_sid,String head_size,String head_dev,String head_protocol_ver,String head_msg_id,String head_md) {
        URL u;
        HttpsURLConnection connection = null;
        try {
            SSLContext sslContext = getSSLContext(context);
            if (sslContext != null) {
                u = new URL(url);
                connection = (HttpsURLConnection) u.openConnection();
                connection.setRequestMethod(method);//"POST" "GET"
                connection.setDoOutput(true);
                connection.setDoInput(true);
                connection.setUseCaches(false);
              
              //此處代碼和業(yè)務相關,請忽略
                connection.setInstanceFollowRedirects(true);
                connection.setRequestProperty("sid",head_sid);
                connection.setRequestProperty("size",head_size);
                connection.setRequestProperty("dev",head_dev);
                connection.setRequestProperty("protocol-ver",head_protocol_ver);
                connection.setRequestProperty("msg-id",head_msg_id);
                connection.setRequestProperty("md",head_md);
                connection.setRequestProperty("Content-Type", "application/json");
              //此處代碼和業(yè)務相關,請忽略

                connection.setHostnameVerifier( new HostnameVerifier(){
                    public boolean verify(String string,SSLSession ssls) {
                        return true;
                    }
                });
                connection.setDefaultHostnameVerifier( new HostnameVerifier(){
                    public boolean verify(String string,SSLSession ssls) {
                        return true;
                    }
                });

                connection.setSSLSocketFactory(sslContext.getSocketFactory());
                connection.setConnectTimeout(30000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }

至此,可以進行https的訪問了

4.遇到的問題

4.1.javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found

我是因為把兩個證書的搞反了,還有如果要使用私有CA簽發(fā)的證書,必須重寫校驗證書鏈TrustManager中的方法

4.2.java.io.IOException: Hostname 'your url' was not verified

4.2.1.服務端https的證書沒有過審

解決方案:忽略ip的驗證

HttpsURLConnection.setDefaultHostnameVerifier( new HostnameVerifier(){
    public boolean verify(String string,SSLSession ssls) {
            return true;
        }
});

但是我發(fā)現(xiàn)這樣忽略hostname 的驗證,并沒有成功,最后我加了下面的代碼 對服務器證書域名進行強校驗:才成功

 connection.setHostnameVerifier( new HostnameVerifier(){
                    public boolean verify(String string,SSLSession ssls) {
                        return true;
                    }
                });
4.2.2.驗證證書時發(fā)現(xiàn)真正請求和服務器的證書域名不一致。

解決這個問題有兩個方法:
1.重新生成服務器的證書,用真實的域名信息。
2.在客戶端代碼中增加如下代碼,忽略hostname 的驗證。(僅僅用于測試階段,不建議用于發(fā)布后的產(chǎn)品中。)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容