日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Https原理及流程

發(fā)布時間:2024/1/23 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Https原理及流程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文地址
我們知道,HTTP請求都是明文傳輸?shù)?#xff0c;所謂的明文指的是沒有經(jīng)過加密的信息,如果HTTP請求被黑客攔截,并且里面含有銀行卡密碼等敏感數(shù)據(jù)的話,會非常危險。為了解決這個問題,Netscape 公司制定了HTTPS協(xié)議,HTTPS可以將數(shù)據(jù)加密傳輸,也就是傳輸?shù)氖敲芪?#xff0c;即便黑客在傳輸過程中攔截到數(shù)據(jù)也無法破譯,這就保證了網(wǎng)絡(luò)通信的安全。

密碼學(xué)基礎(chǔ)

在正式講解HTTPS協(xié)議之前,我們首先要知道一些密碼學(xué)的知識。

明文: 明文指的是未被加密過的原始數(shù)據(jù)。

密文:明文被某種加密算法加密之后,會變成密文,從而確保原始數(shù)據(jù)的安全。密文也可以被解密,得到原始的明文。

密鑰:密鑰是一種參數(shù),它是在明文轉(zhuǎn)換為密文或?qū)⒚芪霓D(zhuǎn)換為明文的算法中輸入的參數(shù)。密鑰分為對稱密鑰與非對稱密鑰,分別應(yīng)用在對稱加密和非對稱加密上。

對稱加密:對稱加密又叫做私鑰加密,即信息的發(fā)送方和接收方使用同一個密鑰去加密和解密數(shù)據(jù)。對稱加密的特點是算法公開、加密和解密速度快,適合于對大數(shù)據(jù)量進(jìn)行加密,常見的對稱加密算法有DES、3DES、TDEA、Blowfish、RC5和IDEA。
其加密過程如下:明文 + 加密算法 + 私鑰 => 密文
解密過程如下:密文 + 解密算法 + 私鑰 => 明文
對稱加密中用到的密鑰叫做私鑰,私鑰表示個人私有的密鑰,即該密鑰不能被泄露。
其加密過程中的私鑰與解密過程中用到的私鑰是同一個密鑰,這也是稱加密之所以稱之為“對稱”的原因。由于對稱加密的算法是公開的,所以一旦私鑰被泄露,那么密文就很容易被破解,所以對稱加密的缺點是密鑰安全管理困難。

非對稱加密:非對稱加密也叫做公鑰加密。非對稱加密與對稱加密相比,其安全性更好。對稱加密的通信雙方使用相同的密鑰,如果一方的密鑰遭泄露,那么整個通信就會被破解。而非對稱加密使用一對密鑰,即公鑰和私鑰,且二者成對出現(xiàn)。私鑰被自己保存,不能對外泄露。公鑰指的是公共的密鑰,任何人都可以獲得該密鑰。用公鑰或私鑰中的任何一個進(jìn)行加密,用另一個進(jìn)行解密。
被公鑰加密過的密文只能被私鑰解密,過程如下:
明文 + 加密算法 + 公鑰 => 密文, 密文 + 解密算法 + 私鑰 => 明文
被私鑰加密過的密文只能被公鑰解密,過程如下:
明文 + 加密算法 + 私鑰 => 密文, 密文 + 解密算法 + 公鑰 => 明文
由于加密和解密使用了兩個不同的密鑰,這就是非對稱加密“非對稱”的原因。
非對稱加密的缺點是加密和解密花費時間長、速度慢,只適合對少量數(shù)據(jù)進(jìn)行加密。
在非對稱加密中使用的主要算法有:RSA、Elgamal、Rabin、D-H、ECC(橢圓曲線加密算法)等。

HTTPS通信過程

HTTPS協(xié)議 = HTTP協(xié)議 + SSL/TLS協(xié)議,在HTTPS數(shù)據(jù)傳輸?shù)倪^程中,需要用SSL/TLS對數(shù)據(jù)進(jìn)行加密和解密,需要用HTTP對加密后的數(shù)據(jù)進(jìn)行傳輸,由此可以看出HTTPS是由HTTP和SSL/TLS一起合作完成的。

SSL的全稱是Secure Sockets Layer,即安全套接層協(xié)議,是為網(wǎng)絡(luò)通信提供安全及數(shù)據(jù)完整性的一種安全協(xié)議。SSL協(xié)議在1994年被Netscape發(fā)明,后來各個瀏覽器均支持SSL,其最新的版本是3.0

TLS的全稱是Transport Layer Security,即安全傳輸層協(xié)議,最新版本的TLS(Transport Layer Security,傳輸層安全協(xié)議)是IETF(Internet Engineering Task Force,Internet工程任務(wù)組)制定的一種新的協(xié)議,它建立在SSL 3.0協(xié)議規(guī)范之上,是SSL 3.0的后續(xù)版本。在TLS與SSL3.0之間存在著顯著的差別,主要是它們所支持的加密算法不同,所以TLS與SSL3.0不能互操作。雖然TLS與SSL3.0在加密算法上不同,但是在我們理解HTTPS的過程中,我們可以把SSL和TLS看做是同一個協(xié)議。

HTTPS為了兼顧安全與效率,同時使用了對稱加密和非對稱加密。數(shù)據(jù)是被對稱加密傳輸?shù)?#xff0c;對稱加密過程需要客戶端的一個密鑰,為了確保能把該密鑰安全傳輸?shù)椒?wù)器端,采用非對稱加密對該密鑰進(jìn)行加密傳輸,總的來說,對數(shù)據(jù)進(jìn)行對稱加密,對稱加密所要使用的密鑰通過非對稱加密傳輸。

以下圖片來自于limboy的博客

image.png

HTTPS在傳輸?shù)倪^程中會涉及到三個密鑰:

服務(wù)器端的公鑰和私鑰,用來進(jìn)行非對稱加密

客戶端生成的隨機(jī)密鑰,用來進(jìn)行對稱加密

一個HTTPS請求實際上包含了兩次HTTP傳輸,可以細(xì)分為8步。
1.客戶端向服務(wù)器發(fā)起HTTPS請求,連接到服務(wù)器的443端口

2.服務(wù)器端有一個密鑰對,即公鑰和私鑰,是用來進(jìn)行非對稱加密使用的,服務(wù)器端保存著私鑰,不能將其泄露,公鑰可以發(fā)送給任何人。

3.服務(wù)器將自己的公鑰發(fā)送給客戶端。

4.客戶端收到服務(wù)器端的公鑰之后,會對公鑰進(jìn)行檢查,驗證其合法性,如果發(fā)現(xiàn)發(fā)現(xiàn)公鑰有問題,那么HTTPS傳輸就無法繼續(xù)。嚴(yán)格的說,這里應(yīng)該是驗證服務(wù)器發(fā)送的數(shù)字證書的合法性,關(guān)于客戶端如何驗證數(shù)字證書的合法性,下文會進(jìn)行說明。如果公鑰合格,那么客戶端會生成一個隨機(jī)值,這個隨機(jī)值就是用于進(jìn)行對稱加密的密鑰,我們將該密鑰稱之為client key,即客戶端密鑰,這樣在概念上和服務(wù)器端的密鑰容易進(jìn)行區(qū)分。然后用服務(wù)器的公鑰對客戶端密鑰進(jìn)行非對稱加密,這樣客戶端密鑰就變成密文了,至此,HTTPS中的第一次HTTP請求結(jié)束。

5.客戶端會發(fā)起HTTPS中的第二個HTTP請求,將加密之后的客戶端密鑰發(fā)送給服務(wù)器。

6.服務(wù)器接收到客戶端發(fā)來的密文之后,會用自己的私鑰對其進(jìn)行非對稱解密,解密之后的明文就是客戶端密鑰,然后用客戶端密鑰對數(shù)據(jù)進(jìn)行對稱加密,這樣數(shù)據(jù)就變成了密文。

7.然后服務(wù)器將加密后的密文發(fā)送給客戶端。

8.客戶端收到服務(wù)器發(fā)送來的密文,用客戶端密鑰對其進(jìn)行對稱解密,得到服務(wù)器發(fā)送的數(shù)據(jù)。這樣HTTPS中的第二個HTTP請求結(jié)束,整個HTTPS傳輸完成。

數(shù)字證書
通過觀察HTTPS的傳輸過程,我們知道,當(dāng)服務(wù)器接收到客戶端發(fā)來的請求時,會向客戶端發(fā)送服務(wù)器自己的公鑰,但是黑客有可能中途篡改公鑰,將其改成黑客自己的,所以有個問題,客戶端怎么信賴這個公鑰是自己想要訪問的服務(wù)器的公鑰而不是黑客的呢? 這時候就需要用到數(shù)字證書。

在講數(shù)字證書之前,先說一個小例子。假設(shè)一個鎮(zhèn)里面有兩個人A和B,A是個富豪,B想向A借錢,但是A和B不熟,怕B借了錢之后不還。這時候B找到了鎮(zhèn)長,鎮(zhèn)長給B作擔(dān)保,告訴A說:“B人品不錯,不會欠錢不還的,你就放心借給他吧?!?A聽了這話后,心里想:“鎮(zhèn)長是全鎮(zhèn)最德高望重的了,他說B沒問題的話那就沒事了,我就放心了”。 于是A相信B的為人,把錢借給了B。

與此相似的,要想讓客戶端信賴公鑰,公鑰也要找一個擔(dān)保人,而且這個擔(dān)保人的身份必須德高望重,否則沒有說服力。這個擔(dān)保人的就是證書認(rèn)證中心(Certificate Authority),簡稱CA。 也就是說CA是專門對公鑰進(jìn)行認(rèn)證,進(jìn)行擔(dān)保的,也就是專門給公鑰做擔(dān)保的擔(dān)保公司。 全球知名的CA也就100多個,這些CA都是全球都認(rèn)可的,比如VeriSign、GlobalSign等,國內(nèi)知名的CA有WoSign。

那CA怎么對公鑰做擔(dān)保認(rèn)證呢?CA本身也有一對公鑰和私鑰,CA會用CA自己的私鑰對要進(jìn)行認(rèn)證的公鑰進(jìn)行非對稱加密,此處待認(rèn)證的公鑰就相當(dāng)于是明文,加密完之后,得到的密文再加上證書的過期時間、頒發(fā)給、頒發(fā)者等信息,就組成了數(shù)字證書。

不論什么平臺,設(shè)備的操作系統(tǒng)中都會內(nèi)置100多個全球公認(rèn)的CA,說具體點就是設(shè)備中存儲了這些知名CA的公鑰。當(dāng)客戶端接收到服務(wù)器的數(shù)字證書的時候,會進(jìn)行如下驗證:

首先客戶端會用設(shè)備中內(nèi)置的CA的公鑰嘗試解密數(shù)字證書,如果所有內(nèi)置的CA的公鑰都無法解密該數(shù)字證書,說明該數(shù)字證書不是由一個全球知名的CA簽發(fā)的,這樣客戶端就無法信任該服務(wù)器的數(shù)字證書。

如果有一個CA的公鑰能夠成功解密該數(shù)字證書,說明該數(shù)字證書就是由該CA的私鑰簽發(fā)的,因為被私鑰加密的密文只能被與其成對的公鑰解密。

除此之外,還需要檢查客戶端當(dāng)前訪問的服務(wù)器的域名是與數(shù)字證書中提供的“頒發(fā)給”這一項吻合,還要檢查數(shù)字證書是否過期等。

通過瀏覽器直接獲取服務(wù)器的公鑰很容易,各個瀏覽器操作大同小異。百度現(xiàn)在已經(jīng)實現(xiàn)了全站點HTTPS,我們就以百度為例如何從Chrome中獲取其公鑰。

用Chrome打開百度首頁,在https左側(cè)我們會發(fā)現(xiàn)有一個綠色的鎖頭。?


點擊該鎖頭,出現(xiàn)一個彈出面板,點擊面板中的“詳細(xì)信息”幾個字。?


這是會打開Chrome的Developer Tool,并自動切換到Security這個頁面。?


點擊“View ceertificate”按鈕就可以查看該網(wǎng)站的證書了,如下所示:?


在“常規(guī)”這個面板中,我們從中可以查看該證書是又Symantec頒發(fā)給baidu.com這個網(wǎng)站的,有效期是從2015年9月17到2016年9月1日。

切換到“詳細(xì)信息”面板,可以查看證書的一些詳細(xì)信息,比如證書所使用的數(shù)字簽名的算法,如下圖所示:?


上面有個“復(fù)制到文件”的按鈕,點擊該按鈕就可以將百度的數(shù)字證書導(dǎo)出成文件,從而我們就可以保存到自己的機(jī)器上,界面如下所示:

我們將其導(dǎo)出成X.509格式的證書,以.cer作為文件擴(kuò)展名,最后保存到本地機(jī)器如下所示:?


切換到“證書路徑”面板,可以查看證書的證書鏈。?


這里先解釋一下什么是證書鏈。我們之前提到,VeriSign是一個全球知名的CA,但是一般情況下,CA不會用自己的私鑰去直接簽名某網(wǎng)站的數(shù)字證書,一般CA會首先簽發(fā)一種證書,然后用這種證書再去簽發(fā)百度等的數(shù)字證書。在此例中,VeriSign簽發(fā)了Symantec證書,然后Symantec又簽發(fā)了baiduc.om,VeriSign位于最頂端,類似于根結(jié)點,因此叫做根CA,Symatec位于中間,叫做中間CA,當(dāng)然,有可能有多個中間CA,這樣從根CA到中間CA,再到最終的網(wǎng)站的證書,這樣自上而下形成了一條證書鏈。如果想要查看證書鏈中的某個證書,只需要選中它,比如選中了Symantec,然后點擊下面的“查看證書”按鈕就會彈出另一個對話框,在其中可以查看Symantec的數(shù)字證書,當(dāng)然也可以將其導(dǎo)出成證書文件保存在硬盤上。

Android中訪問HTTPS
在Android中我們也會經(jīng)常發(fā)送HTTPS請求,這時需要使用HttpsURLConnection這個類,HttpsURLConnection是繼承自HttpURLConnection的,其用法跟HttpURLConnection是一樣的,比如我們想用HTTPS訪問百度的首頁,代碼如下所示:

URL url = new URL("https://www.baidu.com");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
InputStream is = conn.getInputStream();
...
1
2
3
4
我們通過得到的InputStream就可以解析服務(wù)器的返回結(jié)果了。

如果對應(yīng)服務(wù)器的數(shù)字證書存在問題,那么客戶端就無法信任該證書,從而無法建立HTTPS鏈接,我們以國內(nèi)的12306.cn網(wǎng)站為例進(jìn)行說明。

12306.cn的用戶登錄是需要HTTPS的訪問的,如果瀏覽器第一次打開頁面https://kyfw.12306.cn/otn/regist/init,那么瀏覽器要么顯示證書警告信息,要么索性直接不顯示頁面,因為12306.cn的數(shù)字證書存在問題。

其證書鏈如下所示:?


大家可以看到,該12306.cn的證書是由SRCA這個機(jī)構(gòu)簽發(fā)的,也就是說SRCA是證書鏈上的根CA。

但是SRCA是啥呢?沒聽過啊!

我們選中SRCA后,點擊“查看證書”按鈕,SRCA的證書如下所示:?


也就是說SRCA的全稱是Sinorail Certification Authority, 在百度里面搜索該名稱,可以查到一個叫做中鐵信息工程集團(tuán)的網(wǎng)站,http://www.sinorail.com/ProductInfos.aspx?id=185,里面有這么一段描述:?


也就是說SRCA是鐵道部給旗下的網(wǎng)站等做簽名的一個所謂CA,但是它不具備公信力,因為它不是一個全球知名的CA,所以客戶端根本不認(rèn)可它。

我們在Android中直接訪問https://kyfw.12306.cn/otn/regist/init時,會得到如下異常:?
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

這是因為Android的客戶端內(nèi)置的可信任CA列表中沒有包含所謂的SRCA,這樣就出現(xiàn)了12306.cn的證書不被客戶端信任的異常。

為了解決客戶端不信任服務(wù)器數(shù)字證書的問題,網(wǎng)絡(luò)上大部分的解決方案都是讓客戶端不對證書做任何檢查,這是一種有很大安全漏洞的辦法,如下所示:?


首先定義一個自己的SSLTrustAllManager,其繼承自X509TrustManager,具體來說,用SSLTrustAllManager創(chuàng)建一個SSLContext,然后用SSLContext生成一個SSLSocketFactory,然后通過調(diào)用HttpURLConnectoin的setSSLSocketFactory方法將其給某個具體的連接對象使用。由于SSLTrustAllManager沒有對其中的三個核心方法進(jìn)行具體實現(xiàn),也就是不對證書做任何審查。這樣無論服務(wù)器的證書如何,都能建立HTTPS連接,因為客戶端直接忽略了驗證服務(wù)器證書這一步。

這樣雖然能建立HTTPS連接,但是存在很大的安全漏洞。因為黑客有可能攔截到我們的HTTPS請求,然后黑客用自己的假證書冒充12306.cn的數(shù)字證書,由于客戶端不對證書做驗證,這樣客戶端就會和黑客的服務(wù)器建立連接,這樣會導(dǎo)致客戶端把自己的12306的賬號和密碼發(fā)送給了黑客,所以客戶端不對證書做任何驗證的做法有很大的安全漏洞。

解決此問題的辦法是讓Android客戶端信任12306的證書,而不是不對證書做任何檢查。

我們通過上面提到的方法得到12306的證書12306.cer,將其放到assets目錄下,也就是將12306.cer打包到我們的App中,然后用如下代碼訪問12306的HTTPS站點。

class DownloadThread extends Thread{
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? ? ? HttpsURLConnection conn = null;
? ? ? ? ? ? InputStream is = null;
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? URL url = new URL("https://kyfw.12306.cn/otn/regist/init");
? ? ? ? ? ? ? ? conn = (HttpsURLConnection)url.openConnection();

? ? ? ? ? ? ? ? //創(chuàng)建X.509格式的CertificateFactory
? ? ? ? ? ? ? ? CertificateFactory cf = CertificateFactory.getInstance("X.509");
? ? ? ? ? ? ? ? //從asserts中獲取證書的流
? ? ? ? ? ? ? ? InputStream cerInputStream = getAssets().open("12306.cer");//SRCA.cer
? ? ? ? ? ? ? ? //ca是java.security.cert.Certificate,不是java.security.Certificate,
? ? ? ? ? ? ? ? //也不是javax.security.cert.Certificate
? ? ? ? ? ? ? ? Certificate ca;
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? //證書工廠根據(jù)證書文件的流生成證書Certificate
? ? ? ? ? ? ? ? ? ? ca = cf.generateCertificate(cerInputStream);
? ? ? ? ? ? ? ? ? ? System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
? ? ? ? ? ? ? ? } finally {
? ? ? ? ? ? ? ? ? ? cerInputStream.close();
? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? // 創(chuàng)建一個默認(rèn)類型的KeyStore,存儲我們信任的證書
? ? ? ? ? ? ? ? String keyStoreType = KeyStore.getDefaultType();
? ? ? ? ? ? ? ? KeyStore keyStore = KeyStore.getInstance(keyStoreType);
? ? ? ? ? ? ? ? keyStore.load(null, null);
? ? ? ? ? ? ? ? //將證書ca作為信任的證書放入到keyStore中
? ? ? ? ? ? ? ? keyStore.setCertificateEntry("ca", ca);

? ? ? ? ? ? ? ? //TrustManagerFactory是用于生成TrustManager的,我們創(chuàng)建一個默認(rèn)類型的TrustManagerFactory
? ? ? ? ? ? ? ? String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
? ? ? ? ? ? ? ? TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
? ? ? ? ? ? ? ? //用我們之前的keyStore實例初始化TrustManagerFactory,這樣tmf就會信任keyStore中的證書
? ? ? ? ? ? ? ? tmf.init(keyStore);
? ? ? ? ? ? ? ? //通過tmf獲取TrustManager數(shù)組,TrustManager也會信任keyStore中的證書
? ? ? ? ? ? ? ? TrustManager[] trustManagers = tmf.getTrustManagers();

? ? ? ? ? ? ? ? //創(chuàng)建TLS類型的SSLContext對象, that uses our TrustManager
? ? ? ? ? ? ? ? SSLContext sslContext = SSLContext.getInstance("TLS");
? ? ? ? ? ? ? ? //用上面得到的trustManagers初始化SSLContext,這樣sslContext就會信任keyStore中的證書
? ? ? ? ? ? ? ? sslContext.init(null, trustManagers, null);

? ? ? ? ? ? ? ? //通過sslContext獲取SSLSocketFactory對象
? ? ? ? ? ? ? ? SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
? ? ? ? ? ? ? ? //將sslSocketFactory通過setSSLSocketFactory方法作用于HttpsURLConnection對象
? ? ? ? ? ? ? ? //這樣conn對象就會信任我們之前得到的證書對象
? ? ? ? ? ? ? ? conn.setSSLSocketFactory(sslSocketFactory);

? ? ? ? ? ? ? ? is = conn.getInputStream();
? ? ? ? ? ? ? ? //將得到的InputStream轉(zhuǎn)換為字符串
? ? ? ? ? ? ? ? final String str = getStringByInputStream(is);
? ? ? ? ? ? ? ? handler.post(new Runnable() {
? ? ? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? ? ? ? ? textView.setText(str);
? ? ? ? ? ? ? ? ? ? ? ? btn.setEnabled(true);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? });
? ? ? ? ? ? }catch (Exception e){
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? final String errMessage = e.getMessage();
? ? ? ? ? ? ? ? handler.post(new Runnable() {
? ? ? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? ? ? ? ? btn.setEnabled(true);
? ? ? ? ? ? ? ? ? ? ? ? Toast.makeText(MainActivity.this, errMessage, Toast.LENGTH_LONG).show();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? });
? ? ? ? ? ? }finally {
? ? ? ? ? ? ? ? if(conn != null){
? ? ? ? ? ? ? ? ? ? conn.disconnect();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
上面的注釋寫的比較詳細(xì),此處我們還是對以上代碼進(jìn)行一下說明。

首先從asserts目錄中獲取12306.cer證書的文件流,然后用CertificateFactory的generateCertificate方法將該文件流轉(zhuǎn)化為一個證書對象Certificate,該證書就是12306網(wǎng)站的數(shù)字證書。

創(chuàng)建一個默認(rèn)類型的KeyStore實例,keyStore用于存儲著我們信賴的數(shù)字證書,將12306的數(shù)字證書放入keyStore中。

我們獲取一個默認(rèn)的TrustManagerFactory的實例,并用之前的keyStore初始化它,這樣TrustManagerFactory的實例也會信任keyStore中12306.cer證書。通過TrustManagerFactory的getTrustManagers方法獲取TrustManager數(shù)組,該數(shù)組中的TrustManager也會信任12306.cer證書。

創(chuàng)建一個TLS類型的SSLContext實例,并用之前的TrustManager數(shù)組初始化sslContext實例,這樣該sslContext實例也會信任12306.cer證書。

通過sslContext獲取SSLSocketFactory對象,將sslSocketFactory通過setSSLSocketFactory方法作用于HttpsURLConnection對象,這樣conn對象就會信任keyStore中的12306.cer證書。這樣一來,客戶端就會信任12306的證書,從而正確建立HTTPS連接。

以上的處理過程是Google官方建議的流程,步驟流程總結(jié)如下:

證書文件流 InputStream -> Certificate -> KeyStore -> TrustManagerFactory -> TrustManager[] -> SSLContext -> SSLSocketFactory -> HttpsURLConnection

以上步驟的起點是獲取證書文件的文件流,不一定非要從assets目錄中獲取,也可以通過其他途徑得到,只要能拿到證書的文件流即可。

上面的過程是對的,但是還存在一點問題。我們將12306網(wǎng)站自身的12306.cer放到了assets目錄中,然后讓我們創(chuàng)建的HttpsURLConnection的實例信任了12306.cer。但是,數(shù)字證書都是有過期時間的,如果12306網(wǎng)站的數(shù)字證書到期了,那么12306會去SRCA那里重新生成一個數(shù)字證書,這時候12306網(wǎng)站的公鑰和私鑰都會更新,那這樣就存在問題了。我們App的assets目錄中存儲的是老的12306.cer證書,這樣12306網(wǎng)站重新生成了新的數(shù)字證書,那么老的數(shù)字證書就自動作廢了,因為我們App中的12306.cer中的老的公鑰無法解密12306網(wǎng)站最新的私鑰了(公鑰和私鑰只能成對出現(xiàn),舊的公鑰只能解密舊的私鑰)。

很不幸的是,網(wǎng)上大部分的解決方案就是直接信任12306.cer這種網(wǎng)站自身的數(shù)字證書,雖然這種辦法暫時可以解決HTTPS問題,但是不是長久之計,會為以后的數(shù)字證書的更新埋下隱患。

那怎么解決這個問題呢?

最好的辦法不是讓我們的App直接信任12306.cer,而是讓我們的App直接信任12306數(shù)字證書的簽發(fā)者SRCA的數(shù)字證書。

我們用之前提到過的辦法將12306的簽發(fā)者SRCA的數(shù)字證書導(dǎo)出,取名SRCA.cer,將其放到assets目錄下,我們只需要改一行代碼里面的參數(shù)即可,我們將代碼:

InputStream cerInputStream = getAssets().open("12306.cer");
1
修成該

InputStream cerInputStream = getAssets().open("SRCA.cer");
1
也就是我們讀取的是SRCA.cer證書的文件流,而不再是12306.cer的。

通過讓HttpsURLConnection實例信任SRCA.cer,也能夠正常建立HTTPS連接,這是為什么呢?

我們之前提到了證書鏈的概念,假設(shè)存在如下證書鏈:?
CA -> A -> B -> C

CA是根CA證書,例如SRCA.cer,C是最終的網(wǎng)站的數(shù)字證書,例如12306.cer,A和B都是中間證書,理論上來說,只要客戶端信任了該數(shù)字證書鏈中的任何一個證書,那么C證書都會被信任。比如客戶端信任了根證書CA,由于CA信任A,所以客戶端也會信任A,由于A信任B,那么客戶端也信任B,由于B信任C,那么客戶端也信任C。所以在12306的例子中,只要信任了SRCA.cer,那么客戶端就信任12306網(wǎng)站自身的12306.cer數(shù)字證書了。

Android客戶端不信任服務(wù)器證書的原因主要是因為客戶端不信任證書鏈中的根證書CA。12306網(wǎng)站的自身的數(shù)字證書可能會過幾年就會重新生成,發(fā)生變動,但是SRCA作為其簽發(fā)者,發(fā)生變動的次數(shù)會少的多,或者說是很長時間內(nèi)不會改動,所以我們的App去信任SRCA.cer比直接去信任12306.cer要更穩(wěn)定一些。

總結(jié):

HTTPS的傳輸過程涉及到了對稱加密和非對稱加密,對稱加密加密的是實際的數(shù)據(jù),非對稱加密加密的是對稱加密所需要的客戶端的密鑰。

為了確??蛻舳四軌虼_認(rèn)公鑰就是想要訪問的網(wǎng)站的公鑰,引入了數(shù)字證書的概念,由于證書存在一級一級的簽發(fā)過程,所以就出現(xiàn)了證書鏈,在證書鏈中的頂端的就是根CA。

Android客戶端不信任服務(wù)器證書的原因主要是因為客戶端不信任證書鏈中的根證書CA,我們應(yīng)該讓我們的App去信任該根證書CA,而不是直接信任網(wǎng)站的自身的數(shù)字證書,因為網(wǎng)站的數(shù)字證書可能會發(fā)生變化。

希望本身對大家有所幫助!

參考:?
Limboy的《圖解HTTPS》?
阮一峰的《數(shù)字簽名是什么?》?
Google官方的《Security with HTTPS and SSL》
?

總結(jié)

以上是生活随笔為你收集整理的Https原理及流程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。