1、TCP/IP 基本知識
一、概念
TCP/IP協議是一個協議的集合,它是由眾多的互聯網相關聯的協議集合的總稱。如圖
二、TCP/IP分層管理
TCP/IP模型分為5層:應用層,傳輸層,網絡層,數據鏈路層,物理層。(注意:OSI分層是分為七層的)分層的最大好處,就是各層負責各層的任務,一旦某一塊出現問題,則可以直接替換對應的層即可,無需全部更改。
應用層:應用層是我們平時接觸最多的層,它的作用就是向用戶提供應用服務時通信的活動。比如說http協議,FTP協議,dns(域名解析)等協議,都是在該層
傳輸層:主要作用是提供處於網絡連接中的兩台計算機之間的數據傳輸。如tcp(可靠的傳輸控制協議),udp(用戶數據報協議)。傳輸單位是報文段。
網絡層:網絡層用來處理網絡中流動的數據包,數據包是網絡傳輸的最小單位。比如我們常用的IP協議,icmp協議,arp協議(通過IP地址得出對應的mac地址)都在該層。
數據鏈路層:一般用來處理連接硬件部分,如控制網卡,硬件相關的設備驅動。單位是數據幀
物理層:負責數據傳輸的硬件。比如說光纖等等
三、TCP/IP通信傳輸流
1、tcp/ip協議進行網絡通信時,會通過分層順序和對方進行通信。發送端從應用層往下走,接收端則從從下往上走。
2、發送端在層與層之間傳遞數據時,沒經過一層,就會打上相應層所屬的首部信息。而在接收端層與層之間傳遞數據時,沒經過一層,則會把對應的首部信息消去。這種把數據包裝起來的做法,叫做封裝。
四、和http關系密切的協議:IP、TCP、DNS
IP:IP位於網絡層,它是一種協議的名稱。注意和IP地址區分。它主要的作用就是把各種數據包傳遞給對方。要保證准確的傳遞到對方手中,其中有兩個重要的條件:1、IP地址 2、MAC地址 ,(IP地址可以和Mac地址進行配對,IP地址可以變換,但MAC地址,一般是不會變的)。
TCP:TCP位於傳輸層,是可靠的傳輸協議(連接需要三次握手,斷開需要四次揮手),它主要是提供可靠的字節流服務。1、字節流服務:為了傳輸方便,將大塊的數據分割以報文段為單位的數據包進行管理。 2、可靠的傳輸服務:它能夠把數據准確可靠的傳給對方
DNS服務:該服務是位於應用層的,主要的作用就是解析域名,可以將域名解析出對應的IP地址。
五、各種協議與HTTP協議之間的關系
2、http協議
一、http協議特點
http是位於應用層的一個超文本傳輸協議,基於tcp/ip通信協議來傳遞數據的。具有以下特點
1、簡單快速:客戶端向服務端請求服務時,只需要傳遞請求方法和路徑。由於http協議簡單,使得http服務器的程序規模小,因此通信速度快。
2、靈活:http允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type標記。
3、無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求以后,並且收到客戶端的應答后,就端開連接了。
4、無狀態:http協議是無狀態協議,也就是說對事務處理沒有記憶能力,缺少狀態,如果后續處理需要前面的信息,則必須要重新傳遞,這樣可能導致每次連接傳輸的數據量增大,但是在服務器不需要先前的數據的時候,就會快的多了。
5、支持B/S,C/S模式
二、http請求報文和響應報文
請求報文:http請求報文主要有請求行,請求頭,空行,請求體四部分組成。
1、請求行:由請求方法(HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT),URL和協議版本組成。
2、請求頭:為請求報文添加一些信息,由名/值組成
3、空行:請求頭的最后會有一個空行,代表請求頭部結束,接下來是請求正文,此部分不可少。
4、請求正文
響應報文:http的響應報文由狀態行,響應頭部,空行,響應體組成
1、狀態行:由服務器HTTP協議版本,響應狀態碼,狀態碼的文本描述組成
2、響應頭部:和請求頭一樣,由名/值組成
3、請求頭的最后會有一個空行,代表請求頭部結束,接下來是請求正文,此部分不可少。
4、響應正文
請求方法
請求頭信息
響應頭
狀態碼分類
3、https
眾所周知,http是不安全的,它有着很大的缺陷,比如通信使用明文,不驗證通信方的身份,無法證明報文的正確性等。所以就有了https。https並不是一個新的協議。https全稱HTTP over TLS。這個TLS位於傳輸層的上層,應用層的下層,作為一個安全層而存在。
注:對於TLS和SSL的區別,大家可以自己去學習。在這里,只需要理解為TLS是SSL的升級版本就好。
一、https怎么加密的?
TLS是基於X.509認證的,他假定所有的數字證書都是由一個層次話的數字證書認證機構發出,即CA。他可以通過加密技術(對稱加密和非對稱加密)對我們傳輸的數據進行加密。不了解對稱加密和非對稱加密的朋友,可以看看我以前寫的這篇文章:https://www.cnblogs.com/huangjialin/p/9694488.html CA用自己的私鑰簽發數字證書,數字證書中包含有公鑰,然后加密房就可以使用證書中的公鑰解密出CA簽發的證書,從中拿到合法的公鑰。是不是感覺有點懵逼。簡單來說,就是我們一般會在本地內置一個CA證書,然后用本地的CA證書中的公鑰去解密。拿到合法的公鑰,然后就可以用公鑰進行加密數據,然后服務端,就可以使用自己的私鑰就行解密拿到相關的數據。
注意:如果不內置本地證書可以嗎?可以,但是會存在比較大的漏洞,也就是很容易通過中間人攻擊,來截獲數據,所有,內置本地證書並且及時更新證書,這是一種比較有效的防止中間人攻擊的手段。
說了這么多,我們使用代碼來解釋一下,拿okhttp來做例子。okhttp添加加密證書的過程。
對okHttp進行簡單的配置,對OKhttp不熟悉的朋友可以看看這篇文章:https://www.cnblogs.com/huangjialin/p/9469373.html
1 OkHttpClient.Builder builder = new OkHttpClient.Builder() 2 .sslSocketFactory() //配置ssl文件 3 .connectTimeout(15, TimeUnit.SECONDS) 4 .writeTimeout(20, TimeUnit.SECONDS) 5 .readTimeout(20, TimeUnit.SECONDS); 6 OkHttpClient okHttpClient = builder.build(); 7 Request request = new Request.Builder() 8 .get() //設置請求模式 9 .url("https://www.baidu.com/") 10 .build(); 11 12 Call call = okHttpClient.newCall(request); 13 call.enqueue(new Callback() { 14 @Override 15 public void onFailure(Call call, IOException e) { 16 Log.d("MainActivity", "-----------onFailure-----------"); 17 } 18 19 @Override 20 public void onResponse(Call call, Response response) throws IOException { 21 Log.d("MainActivity", "----onResponse----" + response.body().toString()); 22 runOnUiThread(new Runnable() { 23 @Override 24 public void run() { 25 Toast.makeText(MainActivity.this, "請求成功", Toast.LENGTH_LONG).show(); 26 } 27 }); 28 29 } 30 });
okhttp提供了一個方法sslSocketFactory(),專門用來設置ssl的。它需要傳遞一個SSLSocketFactory。
1 private synchronized SSLSocketFactory getDefaultSSLSocketFactory() { 2 try { 3 SSLContext sslContext = SSLContext.getInstance("TLS"); 4 sslContext.init(null, null, null); 5 return sslContext.getSocketFactory(); 6 } catch (GeneralSecurityException e) { 7 throw new AssertionError(); 8 } 9 }
上面的使用的是默認的SSLSocketFactory,也就是說什么也沒有配置,系統提供的。在校驗系統服務器的時候,會信任設備內置的100多個證書。那么他是怎么校驗的呢?主要是通過TrustManager這個類。在上面代碼sslContext.init(null, null, null);中,我們都填了null作為參數,但是這樣的話,都沒有辦法進行證書的校驗。我們看看內部源碼
1 public final void init(KeyManager[] km, TrustManager[] tm, 2 SecureRandom random) 3 throws KeyManagementException { 4 contextSpi.engineInit(km, tm, random); 5 }
1 private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() { 2 try { 3 SSLContext sslContext = SSLContext.getInstance("TLS"); 4 sslContext.init(null, new TrustManager[]{ 5 new X509TrustManager() { 6 public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { 7 8 } 9 10 public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { 11 } 12 13 public X509Certificate[] getAcceptedIssuers() { 14 return new X509Certificate[0]; 15 } 16 } 17 }, null); 18 return sslContext.getSocketFactory(); 19 } catch (GeneralSecurityException e) { 20 throw new AssertionError(); 21 } 22 }
實際上這樣配置,基本算是完成了。也沒錯,但是使用系統默認提供的SSLSocketFactory,它會默認設備中內置的100多個證書。基本上是什么證書都信任了,所以,還是存在很大的風險,中間人很容易就攻擊了。所以,我們還得自己配置自己的SSL證書。
1 private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() { 2 try { 3 // 取到證書的輸入流 當然這里不一定要這樣讀取,就看你的證書存放在哪里了,huangjialin.crt是證書的名稱 4 InputStream is = new FileInputStream("huangjialin.crt"); 5 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 6 Certificate ca = cf.generateCertificate(is); 7 8 // 創建 Keystore 包含我們的證書 9 String keyStoreType = KeyStore.getDefaultType(); 10 KeyStore keyStore = KeyStore.getInstance(keyStoreType); 11 keyStore.load(null); 12 keyStore.setCertificateEntry(null, ca); 13 14 // 創建一個 TrustManager 僅把 Keystore 中的證書 作為信任的錨點 15 String algorithm = TrustManagerFactory.getDefaultAlgorithm(); 16 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm); 17 trustManagerFactory.init(keyStore); 18 TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); 19 20 // 用 TrustManager 初始化一個 SSLContext 21 SSLContext sslContext = SSLContext.getInstance("TLS"); 22 sslContext.init(null, trustManagers, null); 23 24 return sslContext.getSocketFactory(); 25 } catch (GeneralSecurityException e) { 26 throw new AssertionError(); 27 } catch (FileNotFoundException e) { 28 e.printStackTrace(); 29 return null; 30 } catch (IOException e) { 31 e.printStackTrace(); 32 return null; 33 } 34 }
通過我們自定義的SSL,自己設置信任錨點,安全性才會大大的提升。