Android網路編程系列 一 JavaSecurity之JSSE(SSL/TLS)

來源:互聯網
上載者:User

標籤:

要:    Java Security在Java存在已久了而且它是一個非常重要且獨立的版塊,包含了很多的知識點,常見的有MD5,DigitalSignature等,而Android在Java Seurity之外,拓展了一個android.security包,此包中就提供了KeyChain。它包含了主要三個重要的規範:JavaCryptography Extension(簡寫為JCE),JCE所包含的內容有加解密,金鑰交換,訊息摘要(Message Digest,比如MD5等),密鑰管理等。本文所涉及的大部分內容都屬於JSSE的範疇,JSSE所包含的內容就是Java層的SSL/TLS。簡單點說,使用JSSE就可以建立SSL/TLS socket了。JavaAuthentication and Authorization Service(簡寫為JAAS),JSSA和認證/授權有關,這部分內容在用戶端接觸得會比較少一點。    在上述三個子模組或規範中,JCE是JavaSecurity的大頭,其他兩個子模組JSSE和JAAS都依賴於它,比如SSL/TLS在工作過程中需要使用金鑰組資料進行加解密,那麼密鑰的建立和使用就依靠JCE子模組了。但是本篇將主要JSSE版塊中的SSL/TLS協議,重點關注SSL/TLS協議的安全性以及通訊細節詳解,同時就Android或Java程式中對此應用對比並加以輔助說明。   一 : 簡介    SSL (Secure Socket Layer)即:安全通訊端層, 它是由Netscape 所研發,用以保障在 Web的安全傳輸協議,目的是為網路通訊提供機密性、認證性及資料完整性保障,利用資料加密 (Encryption) 技術,可確保資料在網路上之傳輸過程中不會被截取及竊聽。它已被廣泛地用於 Web 瀏覽器與伺服器之間的身份認證和加密資料傳輸。 SSL 協議位於 TCP/IP 協議與各種應用程式層協議之間,為資料通訊提供安全支援。先今,SSL已經成為互連網保密通訊的工業標準。     SSL最初的幾個版本(SSL 1.0、SSL2.0、SSL 3.0)由網景公司設計和維護,從3.1版本開始,SSL協議由網際網路工程任務小組(IETF)正式接管,並更名為TLS(Transport Layer Security),發展至今已有TLS 1.0、TLS1.1、TLS1.2這幾個版本。   如TLS名字所說,SSL/TLS協議僅保障傳輸層安全。同時,由於協議自身特性(數位憑證機制),SSL/TLS不能被用於保護多跳(multi-hop)端到端通訊,而只能保護點到點通訊。   SSL/TLS協議能夠提供的安全目標主要包括如下幾個:   認證性——藉助數位憑證證明伺服器端和用戶端身份,防止身份偽造,確保資料發送到正確的 客戶機和伺服器   機密性——藉助加密防止第三方竊聽   完整性——藉助訊息認證碼(MAC)保障資料完整性,防止訊息篡改   重放保護——通過使用隱式序號防止重放攻擊   為了實現這些安全目標,SSL/TLS協議被設計為一個兩階段協議,分為握手階段和應用階段:   握手階段也稱協商階段,在這一階段,用戶端和伺服器端會認證對方身份(依賴於PKI體系,利用數位憑證進行身份認證),並協商通訊中使用的安全參數、密碼套件以及MasterSecret。後續通訊使用的所有密鑰都是通過MasterSecret產生。   在握手階段完成後,進入應用階段。在應用階段通訊雙方使用握手階段協商好的密鑰進行安全通訊。        SSL/TLS協議有一個高度模組化的架構,分為很多子協議,如所示:


  Handshake協議:包括協商安全參數和密碼套件、伺服器身份認證(用戶端身份認證可選)、金鑰交換;

  ChangeCipherSpec 協議:一條訊息表明握手協議已經完成;

  Alert 協議:對握手協議中一些異常的錯誤提醒,分為fatal和warning兩個層級,fatal類型的錯誤會直接中斷SSL連結,而warning層級的錯誤SSL連結仍可繼續,只是會給出錯誤警告;

  Record 協議:包括對訊息的分段、壓縮、訊息認證和完整性保護、加密等。   二:SSL/TSL協議流程    SSL/TSL協議可以分為以下兩種類型:1>.單向認證    用戶端和伺服器端只存單方面認證 即:用戶端需要伺服器端的認證認證,而服務端則不需要用戶端認證認證。起先,用戶端會發送一條打招呼資訊裡面攜帶了版本、加密體系、壓縮演算法等等相關資訊,服務端收到後,向用戶端確認選擇的版本、加密體系等等資訊。隨後服務端會將自己的認證包含了公開金鑰等資訊發給用戶端,用戶端在取得伺服器端的公開金鑰,將會對要發送的資料進行加密,並發送給伺服器。伺服器端收到後,會用本地私密金鑰對收到的用戶端加密資料進行解密。然後,通訊兩邊都應用這些資料來產生兩邊之間通訊的加密金鑰。接下來,兩邊就可以開始通訊過程了。 2>.雙向認證    雙向認證和單向基本類似,只不過多個幾個認證環節。既然是雙向認證,也就意味著服務端也需要擷取client端認證從而取得用戶端的公開金鑰。隨後用戶端會發送一條用自己私密金鑰加密的資料給服務端,服務端收到後就會用其獲得的公開金鑰進行解密認證。其它環節都跟單向一樣咯。   是一個典型的TLS 1.0協議互動流程如所示:        每一個SSL/TLS連結都是從握手開始的,握手過程包含一個訊息序列,用以協商安全參數、密碼套件,進行身份認證以及金鑰交換。握手過程中的訊息必須嚴格按照預先定義的順序發生,否則就會帶來潛在的安全威脅。     ClientSayHi:首先client端發出這樣的一個訊息,此訊息中攜帶了用戶端所支援的密碼套件列表、最高SSL/TLS協議版本列表、密碼編譯演算法種類列表以及壓縮演算法等等伺服器和用戶端之間通訊所需要的各種資訊。
    此外ClientSayHi中還包含一個隨機數,這個隨機數由4個位元組的當前GMT UNIX時間以及28個隨機播放的位元組組成,共32位元組。該隨機數會在伺服器端公開金鑰產生過程中被使用。ClientSayHi中還可能包含用戶端支援的TLS擴充。(TLS擴充可以被用來豐富TLS協議的功能或者增強協議的安全性)
    ServerSayHi:伺服器接受到後,會返回ServerSayHi訊息。伺服器從用戶端在ClientSayHi訊息中提供的密碼套件、SSL/TLS版本、密碼編譯演算法等列表裡選擇它所支援的項,並把它的選擇包含在ServerSayHi中告知用戶端。接下來SSL協議的建立就基於伺服器選擇的密碼套件類型、SSL/TLS協議版本以及密碼編譯演算法。ServerSayHi中同樣會包含一個隨機數,同樣4+28 位元組類型,由伺服器產生。
  Certificate:用戶端和伺服器都可以發送認證訊息來證明自己的身份,但是通常用戶端認證不被使用。 伺服器一般在ServerSayHi後會接著發一條Certificate訊息,該認證則包含了資料加密演算法種類、server端的產生的隨機數以及公開金鑰等等資訊,客戶利用伺服器傳過來的認證提取相關的資訊驗證伺服器的合法性,伺服器的合法性包括:認證是否到期,發行伺服器憑證的 CA 是否可靠,發行者認證的公開金鑰能否正確解開伺服器憑證的 “ 發行者的數位簽章 ” ,伺服器憑證上的網域名稱是否和伺服器的實際網域名稱相匹配。如果合法性驗證沒有通過,通訊將斷開; 目前主流的認證通用格式則是X509認證格式,在用的X.509認證包含Version 1和Version 3兩種版本,其中v1版本的認證存在安全隱患,同時不支援TLS擴充,被逐漸棄用。現在大多數在用的SSL認證都是V3版本。同時認證會附帶與協商好的金鑰交換演算法對應的密鑰。
  ServerKeyExchange:該訊息僅當以下密碼編譯演算法被使用時由伺服器發出(也就是會說當server端在client端提供的密碼編譯演算法列表裡選擇以下列出的演算法之一,接下來server端就會繼續發送一條ServerKeyExchange資訊):  RSA_EXPORT(僅當伺服器的公開金鑰大於512bit時)、DHE_DSS、DHE_DSS_EXPORT、DHE_RSA、DHE_RSA_EXPORT、DH_anon 使用其它金鑰交換演算法時,伺服器不能發送此訊息。ServerkeyExchange訊息會攜帶對應密碼編譯演算法所需要的額外參數資訊,以後這些資訊在client端會進一步升級與servery端約定的密碼編譯演算法。同時這些參數需要被簽過名。
  CertificateRequest:這個訊息通常在要求認證用戶端身份時才會有也就是雙向認證。訊息中包含了認證類型以及可接受的CA列表。

  ServerSayHiDone:伺服器發送這條訊息表明伺服器部分的金鑰交換資訊已經發送完了,等待用戶端的訊息以繼續接下來的步驟。這條訊息只用作提醒,不包含資料域。
  ClientKeyExchange:這條訊息包含的資料與所選用的金鑰交換演算法有關。 在此之前用戶端會隨機產生一個用於後面通訊的 “ 對稱密碼 “也就是臨時秘鑰(key),它有48個位元組,前2個位元組表示用戶端支援的最高協議版本,後46個位元組是隨機播放的。該秘鑰和client端公開金鑰同時調用包含在之前認證中的或者是ServerKeyExchange中的傳過來的server端公開金鑰進行加密,接著就會隨著ClientKeyExchange這條訊息發送給伺服器,伺服器將用自己持有的私密金鑰來解密該"對稱密碼",這將作為兩者以後通訊中的資料加密演算法,保證資料轉送的安全性。如果選擇的金鑰交換演算法是DH或者DHE,則可能有兩種情況:隱式DH公開值:包含在Certificate訊息裡; 顯示DH公開值:公開值是本訊息的一部分。
  CertificateVerify:這條訊息用來證明用戶端擁有之前提交的用戶端認證的私密金鑰,當使用了雙向認證的時候,這條訊息將會攜帶一條用戶端用自己的私密金鑰生產一個數字,伺服器收到這條資料後,會將之前收到用戶端的公開金鑰進行解密驗證。  ChangeCipherSpec 協議:client端接著發一條該訊息表明握手協議已經完成同時也切換到了加密模式了;
  Finished:表明握手階段結束。這是第一條用協商的演算法和密鑰保護的訊息。因為是用協商好的祕密金鑰加密的訊息,它可以用來確認已經協商好的密鑰。同時Finished訊息包含一個verify_data域,可以用來校正之前發送和接收的資訊。 Verify_data域是一個PRF函數的輸出(pseudo-random function)。這個偽隨機函數的輸入為:(1)兩個hash值:一個SHA-1,一個MD5,對之前握手過程中交換的所有訊息做 加密雜湊功能(訊息摘要),在這裡server就可以根據資料的雜湊值和MD5值進行驗證傳輸資料的完整性;(2)the MasterSecret,由預備主要金鑰產生。     伺服器端收到client端ChangeCipherSpec 和Finished訊息之後,接著也會發送一條ChangeCipherSpec 訊息表面伺服器端也切換到了加密模式,握手協議到此結束了。同時也會接著發一條Finished表明已經收到了client 端的Finished請求正式結束握手。此外,Finished 訊息不能夠在ChangeCipherSpec前發送。     我們不難發現在SSL/TSL通訊協定其實就是兩種演算法的使用:對稱式加密和非對稱式加密。在協議握手環節雙方擷取彼此的公開金鑰解析資料從而產生和擷取接下來的通訊秘鑰這裡就是一個非對稱式加密過程。接下來使用產生的通訊秘鑰進行解密通訊資料,這就是一個對稱式加密的過程。     除此之外,以下幾個名詞我們也需要瞭解: 

Key:屬於JCE的範疇,可分為對稱key和非對稱key。對稱key就是接下來的通訊雙方用來加密資料的秘鑰,非對稱key就是公開金鑰和私密金鑰了。其內部表示形式就是一個類,其外部表格示形式就 是一個位元(bit)字串,key就是用來參與加密解密資料的,就像是一把開鎖的鑰匙。

對稱式加密(symmetric cryptography):就是須要兩邊應用一樣的 key 來加密解密訊息演算法,常用密鑰演算法有 Data Encryption Standard(DES)、triple-strength DES(3DES)、Rivest Cipher 2 (RC2)和 Rivest Cipher 4(RC4)。因為對稱演算法效力相對較高,是以 SSL 會話中的敏感性資料都用經由過程密鑰演算法加密。

非對稱式加密(asymmetric cryptography):就是 key 的構成是公開金鑰私密金鑰對 (key-pair),公開金鑰傳遞給對方私密金鑰本身儲存。公開金鑰私密金鑰演算法是互逆的,一個用來加密,另一個可以解密。常用的演算法有 Rivest Shamir Adleman(RSA)、Diffie-Hellman(DH)。非對稱演算法策畫量大鬥勁慢,是以僅實用於少量資料加密,如對祕密金鑰加密,而不合適多量資料的通訊加密。

加密雜湊功能(Cryptographic Hash Functions): 加密雜湊功能與 checksum 功能相類似。區別在於,checksum 用來偵測不測的資料變更而前者用來偵測有心的資料批改。資料被雜湊後產生一小串位元字串,渺小的資料改變將導致雜湊串的變更。發送加密資料時,SSL 會應用加密雜湊功能來確保資料一致性,用來阻攔第三方破損通訊資料完全性。SSL 常用的雜湊演算法有 Message Digest 5(MD5)和 Secure Hash Algorithm(SHA)。

訊息認證碼(Message Authentication Code): 訊息認證碼與加密雜湊功能相類似,在雜湊加密機基礎上它須要將密鑰資訊與加密雜湊功能產生的資料連絡就是雜湊訊息認證碼(HMAC)。若是 A 要確保給 B 發的訊息不被 C 批改,他要按如下步調做 --A 起首要產生一個 HMAC值;,將其添加到原始訊息後面。用 A 與 B 之間通訊的祕密金鑰加密訊息體,然後發送給 B。B 收到訊息後用密鑰解密,然後就通訊資料重新建立一個 HMAC值,將前後兩個值進行對比來斷定訊息是否在傳輸中被批改。

數字(Digital Signature):一個訊息的加密雜湊被建立後,雜湊值用寄件者的私密金鑰加密,加密的成果就是叫做數字。

  三:實際應用說明   這個案例分兩個部分,一個是服務端,另外一個是用戶端。服務端列印出接收到的用戶端資料,即列印用戶端發來的一個字串。   server端:
void initServer(){                             try {                      //擷取ssl協議的安全環境,或者TLS                     SSLContext sContext = SSLContext. getInstance("SSL");                                 //擷取jks演算法格式的秘鑰儲存空間-常見的有JKS, JCEKS,and PKCS12。其中功能比較全的是JCEKS                                        KeyStore store = KeyStore. getInstance("JKS");                                          AssetManager manager = mContext.getAssets();                                          InputStream is = manager.open( "test_key_store");                                                                //將認證匯入到秘鑰儲存空間中,同時給其配置一個開啟密碼。有時候我們需要有不同類型的秘鑰需要多個秘鑰儲存空間來儲存                      store.load( is, "kevin".toCharArray());                                           is.close();                                           //為了管理多個或多種秘鑰儲存空間,這裡引入了一秘鑰管理器這個概念,就是專門管理多個或多種秘鑰儲存空間                     KeyManagerFactory factory = KeyManagerFactory.getInstance(KeyManagerFactory. getDefaultAlgorithm());                                                                //給每個秘鑰管理器的安置的秘鑰儲存空間配置一個擷取密碼                      factory.init( store, "123456".toCharArray());                                           /**                      * 初始化 ssl協議安全環境。 init函數有三個參數,第一個是KeyManager數組,server端需要用它裡面的儲存的私密金鑰來簽署憑證                   * 第二個是TrustManager數組,第三個是SecureRandom,用來建立隨機數的                      * 對於server端而言,它不需要驗證用戶端認證,所以很顯然第一個參數用來建立服務端Socket的,而第二個參數用於建立用戶端Socket(也可以都不填)                      */                      sContext.init( factory.getKeyManagers(), null, null );                                                                //以下就是配置 ip和連接埠來連結                     InetAddress address = Inet4Address. getLocalHost();                                          SSLServerSocket serverSocket = (SSLServerSocket) sContext.getServerSocketFactory().createServerSocket(8666,1,address);                                          Socket socket = serverSocket.accept();                                          InputStream in = socket.getInputStream();                                           byte[] data = new byte[1024];                                           int count = in .read(data );                                          Log. i("TAG", "DATA:"+new String(data , 0, count ));                                           in.close();                                           socket.close();                                           serverSocket.close();                                   } catch (NoSuchAlgorithmException e ) {                      // TODO Auto-generated catch block                      e.printStackTrace();              } catch (KeyStoreException e ) {                      // TODO Auto-generated catch block                      e.printStackTrace();              } catch (CertificateException e ) {                      // TODO Auto-generated catch block                      e.printStackTrace();              } catch (IOException e ) {                      // TODO Auto-generated catch block                      e.printStackTrace();              } catch (UnrecoverableKeyException e ) {                      // TODO Auto-generated catch block                      e.printStackTrace();              } catch (KeyManagementException e ) {                      // TODO Auto-generated catch block                      e.printStackTrace();              }                     }
client端:
void initClient(){               try {                      //擷取ssl協議的安全環境                     SSLContext sContext = SSLContext. getInstance("SSL");                                 //擷取BKS演算法格式的秘鑰儲存空間-Android中通用的格式                      KeyStore store = KeyStore. getInstance("BKS");                                          AssetManager manager = mContext.getAssets();                                           //這裡是使用Java內建的工具 keytool建立的一個認證放在工程assert檔案中                     InputStream is = manager.open( "test_key_store");                                                                //將認證匯入到秘鑰儲存空間中,同時給其配置一個開啟密碼。有時候我們需要有不同類型的秘鑰需要多個秘鑰儲存空間來儲存                      store.load( is, "kevin".toCharArray());                                           is.close();                                                               TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory. getDefaultAlgorithm());                                                                //初始化trustManagerFactory也就是將認證內容匯入進去                      trustManagerFactory.init(store );                                           /**                      * 初始化 ssl協議安全環境。 init函數有三個參數,第一個是KeyManager數組,                   * 第二個是TrustManager數組,第三個是SecureRandom,用來建立隨機數的                      * 對於client端而言,它需要驗證服務端認證,所以只需要可以去而第二個參數(也可以都不填)                      */                      sContext.init( null, trustManagerFactory .getTrustManagers(), null);                                                                //以下就是配置 ip和連接埠來連結                     InetAddress address = Inet4Address. getLocalHost();                                          SSLSocket socket = (SSLSocket) sContext.getSocketFactory().createSocket(address ,8666);                                          OutputStream out = socket.getOutputStream();                                          String data = "hello i‘m spencer but you can call me kevin" ;                                           out.write( data.getBytes());                                           out.close();                                           socket.close();                                   } catch (NoSuchAlgorithmException e ) {                      // TODO Auto-generated catch block                      e.printStackTrace();              } catch (KeyStoreException e ) {                      // TODO Auto-generated catch block                      e.printStackTrace();              } catch (CertificateException e ) {                      // TODO Auto-generated catch block                      e.printStackTrace();              } catch (IOException e ) {                      // TODO Auto-generated catch block                      e.printStackTrace();              } catch (KeyManagementException e ) {                      // TODO Auto-generated catch block                      e.printStackTrace();              }                     }
  四:小結      JSSE介紹就到此為止。有關JCE的內容以後有時間會給他整理出來,以上有哪裡描述不對的或者有疑問的可以說出來一起探討探討!

Android網路編程系列 一 JavaSecurity之JSSE(SSL/TLS)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.