HTTPS的雙向驗(yàn)證

最近在寫一個(gè)數(shù)據(jù)安全傳輸?shù)捻?xiàng)目图贸,把遇到的坑以及怎么解決的,都記錄下來乾闰。

1.為什么要用HTTPS落追?

因?yàn)镠TTP協(xié)議是沒有加密的明文傳輸協(xié)議,如果APP采用HTTP傳輸數(shù)據(jù)涯肩,則會(huì)泄露傳輸內(nèi)容淋硝,可能被中間人劫持,修改傳輸?shù)膬?nèi)容宽菜。為了保護(hù)用戶的信息安全谣膳、保護(hù)自己的商業(yè)利益,減少攻擊面铅乡,我們需要保障通信信道的安全继谚,采用開發(fā)方便的HTTPS是比較好的方式,比用私有協(xié)議要好阵幸,省時(shí)省力花履。

2.HTTPS通信原理

HTTPS是HTTP over SSL/TLS芽世,HTTP是應(yīng)用層協(xié)議,TCP是傳輸層協(xié)議诡壁,在應(yīng)用層和傳輸層之間济瓢,增加了一個(gè)安全套接層SSL/TLS:

Paste_Image.png

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

3.如何使用HTTPS

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

信息安全的基礎(chǔ)依賴密碼學(xué)夺克,密碼學(xué)涉及算法和密鑰箕宙,算法一般是公開的,而密鑰需要得到妥善的保護(hù)铺纽,密鑰如何產(chǎn)生柬帕、分配、使用和回收狡门,這涉及公鑰基礎(chǔ)設(shè)施陷寝。

公鑰基礎(chǔ)設(shè)施(PKI)是一組由硬件、軟件其馏、參與者盼铁、管理政策與流程組成的基礎(chǔ)架構(gòu),其目的在于創(chuàng)造尝偎、管理、分配鹏控、使用致扯、存儲(chǔ)以及撤銷數(shù)字證書。公鑰存儲(chǔ)在數(shù)字證書中当辐,標(biāo)準(zhǔn)的數(shù)字證書一般由可信數(shù)字證書認(rèn)證機(jī)構(gòu)(CA抖僵,根證書頒發(fā)機(jī)構(gòu))簽發(fā),此證書將用戶的身份跟公鑰鏈接在一起缘揪。CA必須保證其簽發(fā)的每個(gè)證書的用戶身份是唯一的耍群。

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

X509證書鏈,左邊的是CA根證書为牍,中間的是RA中間機(jī)構(gòu)哼绑,右邊的是用戶:

Paste_Image.png

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

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

3.3使用HttpsURLConnection進(jìn)行HTTPS通信

獲取SSLContext

/**
     * 獲取SSLContext
     * @param context 上下文
     * @return SSLContext
     */
    private static SSLContext getSSLContext(Context context) {
        try {
            // 服務(wù)器端需要驗(yàn)證的客戶端證書
            KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);
            // 客戶端信任的服務(wù)器端證書
            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  請(qǐng)求方式
     * @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è)務(wù)相關(guān)吵聪,請(qǐng)忽略
                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è)務(wù)相關(guān)凌那,請(qǐng)忽略

                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;
    }

至此,可以進(jìn)行https的訪問了

4.遇到的問題

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

我是因?yàn)榘褍蓚€(gè)證書的搞反了,還有如果要使用私有CA簽發(fā)的證書吟逝,必須重寫校驗(yàn)證書鏈TrustManager中的方法

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

4.2.1.服務(wù)端https的證書沒有過審

解決方案:忽略ip的驗(yàn)證

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

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末新翎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子住练,更是在濱河造成了極大的恐慌地啰,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件讲逛,死亡現(xiàn)場(chǎng)離奇詭異亏吝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)盏混,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門蔚鸥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人许赃,你說我怎么就攤上這事止喷。” “怎么了混聊?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵启盛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)僵闯,這世上最難降的妖魔是什么卧抗? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮鳖粟,結(jié)果婚禮上社裆,老公的妹妹穿的比我還像新娘。我一直安慰自己向图,他們只是感情好泳秀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著榄攀,像睡著了一般嗜傅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上檩赢,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天吕嘀,我揣著相機(jī)與錄音,去河邊找鬼贞瞒。 笑死偶房,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的军浆。 我是一名探鬼主播棕洋,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼乒融!你這毒婦竟也來了掰盘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤赞季,失蹤者是張志新(化名)和其女友劉穎愧捕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碟摆,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年叨橱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了典蜕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡罗洗,死狀恐怖愉舔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伙菜,我是刑警寧澤轩缤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響火的,放射性物質(zhì)發(fā)生泄漏壶愤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一馏鹤、第九天 我趴在偏房一處隱蔽的房頂上張望征椒。 院中可真熱鬧,春花似錦湃累、人聲如沸勃救。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒙秒。三九已至,卻和暖如春宵统,著一層夾襖步出監(jiān)牢的瞬間晕讲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工榜田, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留益兄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓箭券,卻偏偏與公主長(zhǎng)得像净捅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子辩块,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 其實(shí)蛔六,我對(duì)https以前只有一個(gè)大概的了解,最近工作中遇到一個(gè)問題從而將https協(xié)議做了一個(gè)徹底的學(xué)習(xí)和認(rèn)知废亭,下...
    一條魚的星辰大海閱讀 3,482評(píng)論 0 1
  • 本文摘自 騰訊bugly 的文章《全站 HTTPS 來了》,內(nèi)容有修改掌动。 大家在使用百度四啰、谷歌或淘寶的時(shí)候,是否注...
    bnotes閱讀 3,651評(píng)論 1 9
  • 觀日出 小麻雀 起得早 嘰嘰喳喳唱歌謠 寶寶醒來推開窗 紅紅日出共觀賞 小青蛙 小青蛙 呱呱呱 學(xué)唱兒歌把荷夸 荷...
    云霞客閱讀 193評(píng)論 0 1
  • 學(xué)會(huì)尊重時(shí)間,時(shí)間有限眷射,每個(gè)人的長(zhǎng)度都差不多匙赞,如何在有限的生命中撥開紛紛擾擾的假象佛掖,找到命中的歸屬,是值得窮其一生...
    洛根森閱讀 376評(píng)論 1 1