Https通信原理及Android中實用總結


一、背景

Http儼然已經成為互聯網上最廣泛使用的應用層協議,隨着應用形態的不斷演進,傳統的Http在安全性上開始面臨挑戰,Http主要安全問題體現在:
1,信息內容透明傳輸。
2,通信對方的身份不可安全驗證。
3,通信信息可能被篡改。

於是,結合SSL/TLS協議,形成的Https,被廣泛使用。Https現在已經成為業內安全的Http協議的事實標准。

無論是Web前端,還是Android,或iOS客戶端等終端,與服務端進行Https通信時,通信過程與原理都是相通的,面對需要解決的問題也大同小異。本文主要通過梳理Https通信過程,並總結其在Android中的實際應用,加深對Https的理解。


二、Https通信過程

Https涉及到的知識點很多,如果是完整的舊項目從Http遷移到Https,過程也會相對復雜。本文更多的從客戶端的視角,去梳理與Https密切相關的知識體系。其中,加密算法、散列算法,數字證書等,都是非常重要的技術點。

2.1 對稱加密

對稱加密,對於加密和解密算法來說,加密和解密的密鑰是相同的,如同同一把鎖,加密密鑰和解密密鑰都是相同的一把鑰匙。用密鑰加密后,得到加密后的密文,隨后可以直接用其進行解密,得到初始的原文。

對於通信的雙方,通過對稱加密,可以很方便的將通信內容進行加密,然后進行安全傳輸,能夠有效的防止信息在中途被中間人獲取到原文。即使信息中途有被截獲的可能,但只要密鑰沒有泄露,信息本身還是安全的。

鑒於安全、方便、高效等特點,對稱加密被廣泛的使用在信息傳輸過程中。

2.2 非對稱加密

對稱加密存在最大的一個問題,在於如何將對稱密鑰安全的告知到對方。在一個客戶端可以同時和多個服務端通信,一個服務端可以同時和多個客戶端通信的現實環境下,這種關系是多對多的,而對稱密鑰又只能由當前通信的客戶端和服務端所持有,因此,在實際通信過程中,對稱密鑰的協商成為現實中難點。

與對稱加密不同的是,非對稱加密同時具有公鑰和私鑰。如果用公鑰進行加密,則只能通過對應的私鑰去解密,如果用私鑰進行加密,則只能通過對應的公鑰去解密。 在非對稱加密體系中,公約和私鑰是一對活寶,缺少任何一方都是不可以的。私鑰往往都主體對象自己保留,公鑰則可以直接向外界公開。

公私玥的存在,使得非對稱加密具有了相對對稱加密明顯的優點。
1,公鑰直接向外界公開,外界任一主體通過公鑰進行加密,可以直接向持有私鑰的主體安全的發送加密后的密文,私鑰主體拿到密文后,通過自身持有的私鑰對應解密,獲取明文,以此完成信息的安全傳輸過程,且無須面臨對稱加密中對稱密鑰的安全協商問題。

2,私鑰主體通過私鑰對信息進行加密,外界任一主體通過公鑰去進行解密,如果可以成功解密,則能反向確定對應通信對方的身份,相當於完成了身份校驗。以此,非對稱加密具有很好的身份驗證能力。

凡事有利有弊,非對稱加密相比對稱加密,因為本身功能更加強大,在算法設計上對對應的也更加復雜,在加解密效率上,遠低於對稱加密。

2.3 散列算法

散列算法,也稱之為摘要算法或哈希算法,可以將任一數據對象壓縮成數據摘要,對於同一散列算法,壓縮后的數據摘要具有特定的長度和格式,以此形成數據的“指紋”。原始數據對象的任一細小改動,都可能使得新的“數字指紋”有着很大不同。

往往通過散列算法,可以判斷兩個數據對象是否相同,因為相同的數據對象,通過散列算法形成的“數字指紋”必定是相同的。但是相同的“數字指紋”,對應的原始數據對象不一定是相同的,因為可能存在散列沖突問題。

散列算法,在現實中就有廣泛的使用場景,例如最常用的對兩個文件對象進行MD5,判斷其內容是否相同。

嚴格意義上來說,散列算法與加密算法(對稱加密或非對稱加密)是有着本質區別的,加密算法,對應的是可以解密的,目的是進行數據加密后的安全存儲或傳輸,是可以通過密鑰得到原文的,是可逆的過程。而散列算法,本質上“數字指紋”的范疇,通過散列算法形成的“數字簽名”,直接在算法層面是不能得到原文的,是不可逆的。當然,通過彩虹表或數據字典這種形式的所謂解密,本質上只是暴力破解的過程。

2.4 Https通信流程

Https = Http + SSL/TLS = Http + 內容加密 + 身份驗證 + 數據完整性保護。當前,TLS主要使用的版本是1.2,按照RFC官網,Https通信過程示意如下:

 

完整的具體過程可以參照下圖(來源:wiki):

 

 

 

從整體上看,Https通信過程包含了秘鑰協商和加密通信兩個階段。

2.4.1 秘鑰協商

秘鑰協商是整個Https通信過程中的重點部分。此處的秘鑰,指的是客戶端與服務端協商后續用於信息加密的對稱秘鑰。協商的主要過程包括:
1,客戶端向服務端發送ClientHello消息,其中包含客戶端隨機生成的隨機數RNc,客戶端的協議版本信息、加密套件信息等。
2,服務端接收消息,並向客戶端發送ServerHello消息,其中包括服務端隨機生成的隨機數RNs,服務端協議版本信息,加密套件等信息。
3,服務端向客戶端發送包含了服務端公鑰的證書。
4,服務端向客戶端請求包含了客戶端公鑰的證書。
5,客戶端核驗服務端證書,並向服務端發送包含了客戶端公鑰的客戶端證書。
6,服務端核驗客戶端證書。
7,客戶端生成隨機數PMS,並通過服務端公鑰將PMS進行加密,發送給服務端。
8,客戶端和服務端通過同樣的算法,以及RNc,RNs,PMS,分別生成對稱加密秘鑰,因為算法和參數都是相同的,因此,兩端生成的對稱秘鑰也是相同的。

以此完成兩端對稱秘鑰的協商過程。

2.4.2 加密通信

秘鑰協商完成后,SSL握手過程結束。后續的通信過程都基於協商了的對稱秘鑰進行加密,在進行傳輸。這個過程與一般意義上的加密通信過程沒有差別。

秘鑰協商的最終目的,是在客戶端和服務端安全的同時生成用於后續加密通信過程的對稱秘鑰。這也正是Https所謂的安全的根本。

2.5 CA及數字證書

秘鑰協商過程中,客戶端和服務端如何安全的交換彼此的非對稱加密的公鑰成為重點。因為一旦安全的獲知對方的公鑰后,就可以通過對方公鑰進行加密,進一步完成后續對稱秘鑰的協商。在獲得對方公鑰時,如何對通信對方身份進行核驗,以確保此公鑰就是真實的通信對方的公鑰?

公鑰不可能直接通過網絡進行傳輸,否則依然存在信息被中間竊取和篡改的可能,進一步可能引發中間人攻擊。因此,在獲得對方公鑰的同時,還能有一套機制去核驗對方的身份,以及對方公鑰的准確性,是一大難點。

直接通過網絡方式,將永遠是一個雞生蛋和蛋生雞的問題,安全性永遠沒法得到保障。由此,通過基於CA的方式,頒發數字證書,來對通信方的身份進行“擔保”,對數據完整性提供核驗,被設計出來。

通過信任CA,由此信任CA頒發的數字證書,
由此信任通信方並獲取到對方的公鑰。
復制代碼

對應的,現在存在兩個問題:
1,CA本身的身份,如何信任?
2,信任CA后,如何校驗數字證書以確認通信對方的身份並獲取到對應的公鑰?

實際應用中,對CA身份的信任,提供了兩種途徑。一種是直接基於系統的集成,將全球主要的CA本身對自己簽發的數字證書集成到系統中。如Mac電腦系統或Android手機等。另一種是基於人為的手動授信,如通過瀏覽器下載第三方的CA數字證書,並設置對其進行信任。無論是哪種方式,系統將會對集成了的CA數字證書信任,並能獲取到對應的CA公鑰。

接下來以客戶端校驗服務端證書為例,闡述完整的身份核驗和公鑰獲取過程。

管理員向CA申請數字證書,其中包括服務端公鑰,域名,證書有效期,組織機構,地域,證書序列號,頒發機構等信息。這些信息正文,通過散列算法H,形成信息摘要,CA對生成的信息摘要,通過CA自身的非對稱加密的私鑰進行加密,得到密文,此密文稱之為數字簽名(CA對信息摘要進行了簽名),信息正文、數字簽名,放在一起,形成數字證書。

數字證書上,包括兩大內容:
1,信息正文
2,數字簽名。
復制代碼

服務端將證書發送給客戶端后,客戶端首先拿到證書的CA信息,與系統中已經授信的證書CA進行比較,如果發現存在同樣的CA,則使用CA的公鑰對其進行解密。否則將嘗試基於證書鏈的形式對服務端證書CA的身份進行驗證,通過后再基於CA公鑰進行解密。否則,服務端證書校驗失敗。

CA公鑰解密數字證書上的數字簽名后,可以得到消息摘要,再通過對信息正文進行同樣的散列算法,也得到消息摘要,將兩個消息摘要進行對比,以完成數據完整性的校驗。同時,服務端身份以及數字證書上的服務端公鑰也得以確認。


三、Https抓包原理及Charles實踐

3.1 中間人攻擊

Https抓包,本質上是基於中間人攻擊。安裝好抓包軟件,開啟抓包服務,將客戶端(手機或瀏覽器等)配置好相應的代理服務器地址及端口。此時,客戶端的Http/Https請求將通過抓包服務中轉,對Http/Https通信服務端而言,抓包服務充當了客戶端角色,對源客戶端而言,抓包服務充當了服務端角色。因此,抓包服務形成事實上的代理,且同時具有正向代理和反向代理的職能。

通信經過抓包服務,但對於Https請求,由於報文主體是加密的,此時雖然能抓到包,但看到報文主題是亂碼形式。因為此時抓包服務無法對報文主體進行透明解密,也無法達到可以篡改的效果。此時,針對報文主體,更多的扮演的是一個中間代理轉發通信的角色。因為此時,客戶端對通信服務端的身份依然既有有效的核驗過程。

在使用抓包軟件時,都會要求客戶端安裝一個證書並設置信任。這個證書,就是抓包服務所對應的證書,客戶端信任此證書后,相當於完全信任了抓包服務。此時,抓包服務具備了完整意義上的通信中轉、分發、信息透明和篡改等功能,達到了完整意義上中間人攻擊的效果。源客戶端和源服務端實際上將不再是和對方通信,只是他們以為,通信方還是彼此。

信任了抓包服務的證書,此后,源客戶端實際上,是在和抓包服務進行通信,對服務端的身份和信息完整性校驗,也是基於抓包服務的公鑰。源客戶端的請求,對抓包服務將是完全透明的,抓包服務可以篡改此請求,並扮演成源客戶端角色,和源服務端通信,因為抓包服務具有源服務端的公鑰和協商后的對稱加密秘鑰,因此,服務端返回的結果,對抓包服務是透明的,抓包服務可以對其進行篡改,並返回給源客戶端。這也是很多抓包工具,不但具有對通信內容的透明顯示,也還具有額外的調試功能,如通過對請求參數,或返回結果等的修改,達到想要的調試目的等。

主流的Http/Https抓包工具有Fiddler、Charles等。Mac上用的較多的一般是Charles。下面主要總結下Charles實踐部分。

3.2 Charles破解

當前官方版本的Charles 是收費軟件,有30天的免費試用時間,即使試用期過后,未付費的用戶仍然可以繼續使用,但是每次使用時間不能超過 30 分鍾,並且啟動時將會有 10 秒種的延時。實際使用過程中有點不太方便。可能用着用着,需要你重啟。

網上提供了很多Charles破解的資料,在此只是做一個總結,平時使用請支持正版。

最新Charles版本是v4.2.8。

3.2.1 Charles破解工具

Charles在線破解工具:
www.zzzmode.com/mytools/cha…
對應的還有一個歷史版本的破解說明和Github項目地址:
blog.zzzmode.com/2017/05/16/…
github.com/8enet/Charl…

RegisterName輸入自己喜歡的注冊名,然后選擇自己本機已經安裝的Charles版本,點擊“生成”,下載對應生成的charles.jar文件。

覆蓋本機對應Charles安裝文件。/Applications/Charles.app/Contents/Java/charles.jar 重啟Charles,發現顯示“Registered to: corn”,且已經正式激活。

 

 

3.2.2 Charles破解原理

Charles對於是否破解的判斷,是寫在charles.jar文件中的,找到混淆后的對應class文件,將對應破解判斷的邏輯,通過修改字節碼的方式強制改成已經注冊邏輯,然后重新生成charles.jar文件。

3.2.3 Beyond Compare比較.class文件

具體可以通過Beyond Compare對比兩個charles.jar文件,看看具體差異在什么地方。

Beyond Compare默認不支持.class格式的文件比較。因此,默認情況下,如果直接比較,可以發現唯一差異的文件為:qHTb.class,但文件中的內容是識別不了的,以類似亂碼形式展示。

 

 

 

 

Beyond Compare默認支持的文件格式可以通過Beyond Compare >> 文件格式...查看。默認不支持.class格式的文件,我們可以添加其對.class格式的文件支持。

網址:www.scootersoftware.com/download.ph… 上提供了其他的文件格式,我們可以對應搜索並下載后,通過工具 >> 導入配置...給配置進來,以此支持此類文件文件。但.class文件格式只提供了Windows平台的支持,Mac下沒有提供。

 

 

 

 

不過,我們可以通過配置文件格式為外部程序轉換的方式進行額外的格式支持。上述Windows平台中的.class文件支持中,具體可以看到如下描述:

Java class to source8-Feb-2019 *.class - Compares Java class files decompiled to source. - - Uses Java decompiler, Jad: http://varaneckas.com/jad 復制代碼

因此,我們也可以通過直接配置jad的方式,直接對比.class文件。

a, http://varaneckas.com/jad對應的jad版本,解壓后放置到系統合適目錄下,如/Users/corn/Research/jad158g/
b,Beyond Compare >> 文件格式... >> 新增格式化文件General中配置*.class,Conversion中勾選上外部程序,並配置上對應的外部程序命令:/Users/corn/Research/jad158g/jad -p %s >%t

c,再次對比 qHTb.class,發現已經可以直接對比查看了(如果還不行試着選擇導航中的 Format >> class文件)。

 

我們發現,破解的charles.jar,只是修改了qHTb.class字節碼中的DdNM()方法,直接返回了true,此為校驗是否注冊的方法。另一個方法是gbef(),此為獲取注冊名的方法。

由此,通過強制修改字節碼的方式將對應邏輯改成已經注冊,完成破解。

3.3 Charles抓包注意點

Charles抓包,網上實際上有很多文章,都有專門闡述,此處主要總結下抓包過程中,需要注意的地方:
1,源客戶端,需要安裝后Charels對應的證書,並對其信任。具體過程參考菜單Help >> SSL Proxying中。
2,需要具體配置好Proxy Setting,且SSL Proxy Setting也要配置好,這一點經常容易忘。如果想所有的包,可以直接配置*。但需要特別注意的是,如果勾選上Proxy >> MacOS Proxy后,設置的是系統代理模式,可能會對系統的其他軟件的使用造成影響,例如Android Studio。因為此時,AS也很可能走對應的代理(因為可能有緩存)。當然,如果需要抓包國外的一些被牆了的域名地址,也可以通過設置External Proxy Settting ...形式走外部的其他的代理。
3,通過Start Recording,開啟抓包。抓包過程中可以進行延遲、斷點、rewriteMap Remote/Local等各種調試技巧。斷點不一定有效,因為實際中,發現往往會延遲。rewrite、Map實際中,非常有用,例如和服務端聯調等。
4,有些情況下,源客戶端是可以禁掉抓包服務的,例如配置上不使用代理等。此時,Charles等抓包服務將會失效。


四、Android防抓包原理及總結

Android開發中,通過抓包分析競品App的通信信息,是很常見的。但往往也發現,有些App,是抓不了包的。為了自身App的安全性等考慮,正式對外發布的安裝包,有必要做防抓包處理。

抓包的本質,是中間人攻擊。針對中間人攻擊的原理,在其中關鍵環節,打破抓包工具成為完整意義上中間人的條件,便可以實現Android App的防抓包防護。

1,源客戶端忽略代理設置。
Android項目中,可以通過判斷當前是否在使用代理,通過忽略掉代理設置,實現請求的直接連接。
一般的對是否使用代理的判斷寫法如下:

/**
 * 判斷是否是在代理環境下
 *
 * @return */ private boolean isUsingProxy() { // 是否大於等於14 final boolean isIceOrUpper = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; String proxyAddress; int proxyPort; if (isIceOrUpper) { proxyAddress = System.getProperty("http.proxyHost"); String portStr = System.getProperty("http.proxyPort"); proxyPort = Integer.parseInt((portStr != null ? portStr : "-1")); } else { proxyAddress = Proxy.getHost(BaseApplication.context); proxyPort = Proxy.getPort(BaseApplication.context); } return (!TextUtils.isEmpty(proxyAddress)) && (proxyPort != -1); } 復制代碼

OkHttp直接提供了忽略代理設置的接口。如果發現正在使用代理,可以設置忽略代理。

OkHttpClient.Builder builder = new OkHttpClient.Builder();
...
builder.proxy(Proxy.NO_PROXY);
....
復制代碼

2,源客戶端設備提高證書信任級別。
Android 7.0開始,將網絡安全配置中的證書由原來的“系統級證書”和“用戶級證書”改成了“系統級證書”。這也就意味着,“用戶級證書”默認將不被信任,即使用戶自己,已經設置了對其信任。因此,我們會發現,默認情況下,在Android 7.0及以上手機上,是抓不了包的。
抓包代理顯示的信息為:

 

源客戶端顯示的信息為:

Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. at com.android.org.conscrypt.TrustManagerImpl.verifyChain(TrustManagerImpl.java:661) at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:539) at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:605) at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:495) at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:418) at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:339) at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:94) at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:88) at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:197) at com.android.org.conscrypt.ConscryptFileDescriptorSocket.verifyCertificateChain(ConscryptFileDescriptorSocket.java:399) at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method) at com.android.org.conscrypt.SslWrapper.doHandshake(SslWrapper.java:374) at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:217) .... 復制代碼

同時,Android對開發者提供了網絡安全配置入口,如果需要抓包,只需要將“用戶級證書”加入到對應配置項即可。

<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config> <trust-anchors> <certificates src="user" /> <certificates src="system" /> </trust-anchors> </base-config> </network-security-config> 復制代碼

<certificates src="user" />是配置的重點,表示“用戶級證書”可被信任。
開發過程中,或內部的測試包,可能需要抓包,因此,對內環境開啟此配置項是很有必要的。但對外的正式包中,應該保持系統原本配置,以提升抓包防護的安全級別。

針對不同場景下的網絡安全配置需求,官方文檔也給出了解決方法,例如提供了android:debuggable為true情況下的專門配置。但實際App開發中,往往此開關都直接是false配置的,解決方法也很簡單,可以針對App的buildTypesproductFlavors甚至變體去單獨配置network-security-config即可。如針對對內的dev環境下配置上<certificates src="user" />,以使得可以抓包,對外環境下去掉此配置。

3,源客戶端加強對服務端的信任校驗。 源客戶端加強對服務端的信任校驗,實際中根據具體的需求場景,服務端證書的配置方式等,可以有多種方式。例如源客戶端內置上服務端證書,或者對服務域名進行安全校驗,又或者對證書名稱進一步校驗等。一旦發現不符合預期的證書信息項,則終止對應請求。

下面簡單演示對服務端信息判斷,一旦是抓包服務信息,則終止掉請求。

public class ServerCertificateSecurityVerifier implements HostnameVerifier {
    ....
    
    private static final String[] DEFAULT_INSECURITY_CA_ISSUER_KEYWORDS = new String[]{
            // fiddler
            "fiddler.com", // fiddler "fiddler2.com", // charles "charlesproxy.com", // whistle "wproxy.org", // Android Packet Capture "Packet Capture CA Certificate", // mitmproxy "mitmproxy", // debug proxy "Debug Proxy" }; @Override public boolean verify(String hostname, SSLSession session) { // 不允許抓包條件下,判斷正式信息是否包含主流的抓包代理,如果,校驗失敗,否則,走默認流程 try { for (X509Certificate x509Certificate : session.getPeerCertificateChain()) { if (x509Certificate.getIssuerDN() == null || StringUtil.isEmpty(x509Certificate.getIssuerDN().getName())) { continue; } String caIssuerCompleteName = x509Certificate.getIssuerDN().getName(); for (String insecurityCAIssuerKey : mInsecurityCaIssuerKeywords) { // 證書信息中有抓包工具的證書信息 if (Pattern.compile(Pattern.quote(insecurityCAIssuerKey), Pattern.CASE_INSENSITIVE) .matcher(caIssuerCompleteName).find()) { Log.w(, TAG, "ServerCertificate error... - " + caIssuerCompleteName); return false; } } } } catch (Exception e) { Log.e(TAG, e); } return verifyByOkHttpDefault(hostname, session); } 復制代碼

五、結語

網絡請求的安全性問題現在愈發得到重視,Google和Apple也明確要求在其應用市場上上架的App都必須遵循Https安全標准。Google Play渠道上架的條款中,也明確規定,對於邏輯中可能存在的中間人攻擊行為,需要做進一步的安全處理,否則不能上架成功。從Android 7.0開始提升系統默認情況下只支持“系統級證書”等行為上看,Https的安全要求和標准也會不斷提高。平時Android App開發過程中,與服務端通信過程中涉及到的安全性問題,還有很多實際的場景,基於Https基礎上,需要做進一步的加密處理或安全校驗,以盡可能的提高App安全防護級別。





參考網址:
tools.ietf.org/html/rfc524…
developer.android.com/training/ar…
codelabs.developers.google.com/codelabs/an… mp.weixin.qq.com/s/3M0CqFQP2…
zh.wikipedia.org/wiki/傳輸層安全性…
zh.wikipedia.org/wiki/超文本傳輸安…


作者:HappyCorn
鏈接:https://juejin.im/post/5d3528ab6fb9a07ecf7261c2
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM