Https安全通信(一)

iOS 9.0之后蘋果開始要求使用Https進行通信。ATS是iOS9和OS X El Capitan的一個新特性。開啟該功能后,ATS對使用NSURLConnection, CFURL或NSURLSession 等APIs 進行的網絡請求默認強制使用HTTPS加密傳輸,目標是提高Apple 操作系統以及應用程序的安全性。蘋果公司官方文章指出,https必須符合ATS要求,服務器必須支持傳輸層安全(TLS)協議1.2以上版本;證書必須使用SHA256或更高的哈希算法簽名,并使用2048位以上RSA密鑰或256位以上ECC算法等等。

https概要

  • https協議基于http(超文本傳輸協議),在http通信基礎上對傳輸報文進行加密,主要是為了保證通信雙方的數據不被竊取。在通信時,服務器和客戶端各自提供自己的憑證,驗證自己的身份后進行通信. 這樣能減少不受信任的三方竊取信息。
  • PKI(公鑰基礎設施),是HTTPS的基礎,PKI與非堆成秘鑰加密技術密切相關,包括消息摘要,數字簽名,和加密服務。而數字證書以及證書機構(CA -Certificate Authority)是PKI中重要的概念。
  • 數字證書在https中也起著至關重要的作用, 它是一個計算機文件,一般由可信任的證書機構頒發,他包含了證書所有者的一般信息(公開秘鑰)。并且能夠證明這個公鑰確實是證書所有者所合法擁有的。這一點是有CA對數字證書的簽名來保證的(簽名用到了信息摘要以及非對稱加密算法,使用CA的私鑰加密),在這里我門需要使用CA的數字證書(包含CA的公鑰)來驗證簽名的合法性,那么我門如何驗證CA數字證書的合法性呢?CA的信任鏈可以解決這個問題。一個CA的證書是由上一級CA簽發的,因此它的合法性由它的上一級CA來驗證,其中最頂層的證書機構被稱為根CA,它的證書被稱為根證書,如果一個鏈條的根證書是合法可信的,那么我們就認為在這個鏈條上的所有CA以及它們所簽發的證書都是合法可信的。于是,最終的問題就是如何保證根證書的合法性呢?原來,我們所使用的基本計算機軟件,比如瀏覽器和服務器軟件,都會內置根CA的自簽名證書,只要我們使用的基本軟件可信,那么就能保證根CA證書的合法有效。

https圖解

  • https雙向通信,只是在服務器身份確認后,提供客戶端憑證再進行一次客戶端的身份校驗過程。首先客戶端和服務器需要經過三次確認,生成二個隨機數,并將服務器的證書提供給客戶端。前面兩個隨機數因為時明文傳輸的,很容易給截取到,當客戶端驗證完服務器端的證書OK后,通過隨機數擴展算法,按一定的規則生成第三個隨機數,并用服務器的公鑰加密后發送給服務器.這樣客戶端和服務器均獲得三個隨機數,從而可以生成雙方約定的會話秘鑰。 客戶端再將自己的證書發送給服務器端進行驗證,只要服務器驗證通過,那么他們就開始使用這個隨機秘鑰進行數據加密。實際上如果, 客戶端和服務器端協議版本號不一致,客戶端偏好設置中的加密算法列表里沒有服務器支持的任何一種加密算法,客戶端的憑證或服務器的憑證無法匹配,服務器域名不正確,或者證書過期等,都會導致通信失敗,這時候根據指定的警報協議 將相關的失敗信息通知到接收方。
  • 下圖為https的單向通信的主要流程,實際要比這個要復雜的多(忽略上面客戶端憑證的校驗過程)。


    Snip20161204_8.png

https通信詳解

  • HTTPS是工作于SSL層上的HTTP協議,SSL(安全套接層)工作于TCP層上,向應用層提供了兩個基本的安全服務,認證和保密。 SSL有三個子協議, 握手協議,記錄協議,警報協議。
    • 握手協議:
      1. 建立安全能力: 由客戶端發起,向服務器發送Client Hello消息,其中包含SSL版本,客戶端隨機數(用于生成秘鑰),會話號,加密算法清單(客戶端所支持的加密算法),壓縮算法清單。服務器返回 Server Hello信息,其中包含SSL版本,服務器隨機數(用于生成秘鑰),會話號,選擇加密算法,選擇的壓縮算法。
      2. 服務器認證與秘鑰交換服務器是本階段發送信息的所有方,共三步
        • 第一步 : 證書,服務器將數字證書以及CA證書鏈發給客戶端,客戶端由此獲得服務器公鑰;客戶端還可以再這一步驗證服務器是否可信.如果不可信則可以停止鏈接。并提醒用戶注意。
        • 第二步 : 服務器請求客戶端證書,客戶端認證在SSL中是可選的,因此這一步也是可選的。
        • 第三步 : 服務器握手完成,發送這個消息后,服務器等待客戶端的響應。
      3. 客戶端認證與秘鑰交換
        • 客戶端是本階段的所有信息的發送方,分為三步
        • 第一步 : 證書,客戶端將客戶端的數字證書發送給服務器,里面包含了客戶端的公鑰,通常來說這個證書應該是由服務提供著分發給客戶端,由指定的CA簽發的,因此服務器可以驗證客戶端證書的合法性,并決定是否繼續。
        • 第二步 : 秘鑰交換,客戶端生成48字節的預備秘鑰,并用服務器端的公鑰加密,然后發送給服務器,這個預備秘鑰只有客戶端和服務器知道(與雙發在之后會話中使用的對成秘鑰相關),在這一步也間接的驗證了服務器的合法性.因為只有擁有與證書對應的私鑰才能解密出預備秘鑰。
        • 第三步 : 證書驗證,客戶端還需要向服務器驗證自己是真正的客戶端(數字證書無法證明它就是客戶端,擁有閾公鑰對應的私鑰才是關鍵),為此客戶端把預備秘鑰,客戶隨機數,服務器的隨記數組和起來,用私鑰對結果進行簽名,發送給服務器,服務器利用客戶端公鑰就能夠的到原始的數據,用來驗證客戶端的真實性。(這里主要是對 客戶端隨記數,服務器隨記數,預備秘鑰)這個三個要素進行判定是否有人竊取和串改。
      4. 完成: 在這個階段,客戶端和服務器各自獨立生成相同的主秘鑰和對成秘鑰,主秘鑰和對成秘鑰只有它們自己知道。主秘鑰和對成秘鑰是由 預備秘鑰,客戶端隨機數和服務器隨機數組和后,經過消息摘要算法生成。
  • SSL握手完成之后,就會進入回話階段:客戶端和服務器使用握手協議中生成的對成秘鑰進行加密和解密以保證通信的安全。
  • 總的來說, 如果鑰完成HTTPS的雙向認證需要以下秘鑰和證書:
    • 服務器端: 1.服務器私鑰, 2.由CA前發的含有服務器公鑰的數字證書, 3.CA的數字證書,在雙向驗證過程中通常服務器可以自己作為證書機構,并且由服務器CA前發服務器證書和客戶端證書。
    • 客服端: 1.客戶端私鑰, 2. 由CA簽發的含有客戶端公鑰的數字證書。為了避免中間人攻擊,客戶端還需要內置服務器證書,用來驗證所連接的服務器是否是指定的服務器。(通常在一些服務器的網站或者app中,客服端憑證校驗是默認省略的,單向安全通信的,只對服務器的身份進行校驗)。

https實踐

  • 在Apple Reference《Certificate,Key,and Trust Services Programming Guide》提供了https驗證的框架, security.該框架包含了 https通信中信任對象, 同時也提供https加密中SAH,MD5,RSA,AES等算法的支持。
  • 基于AFNetworking https單向驗證,系統框架已經做了非常多的簡化步驟,在AFSecurity這個庫中,框架內部已經做好了相關的判定,比如根據是否為私有證書,將提供的 證書對象設置為錨證書,對服務器返回的 信任對象進行評估,如果通過則進行安全通信,反之則取消通信。
       + (AFSecurityPolicy*)configSecurityPolicy {
         NSString *cerPath = [[NSBundle mainBundle]                 pathForResource:@"https" ofType:@"cer"];//證書的路徑
         NSData *certData = [NSData dataWithContentsOfFile:cerPath];
         AFSecurityPolicy* securityPolicy = [AFSecurityPolicy    policyWithPinningMode:AFSSLPinningModeCertificate];
         securityPolicy.allowInvalidCertificates = YES;  /**如果采用三方頒發的證書,則不使用自建證書驗證服務器,由三方機構驗證*/
         securityPolicy.validatesDomainName = YES;
         securityPolicy.pinnedCertificates  = [NSSet setWithObject:certData];
         mgr.securityPolicy = securityPolicy;
         return securityPolicy;
         }
  • 基于AFNetworking雙向通信。
    • 在AFNetworking中進行雙向通信,需要我們自己對挑戰認證進行定義,其中服務器身份驗證部分,我們仍然可以沿用上面的 security對象進行自動校驗,但是客戶端驗證,就需要我們進行實現了。
    • AFNetworking框架已經為我們講調整驗證的方法是用回調Block抽離出來。在使用時候我們只需鑰注冊該回調block,在對應的block中實現當收到驗證服務器端憑證和需要提供客服端憑證的方法進行處理.相關代碼實現方式如下.
    • 服務端驗證: 首先需要對挑戰challege對象的保護空間進行判定,獲取當前的驗證方式,如果是對服務器驗證,則取出serverTrust對象,并通過設定錨證書,與我們定義的服務器證書的信任對象進行比對和評估,判定是否正確,如果正確,則告訴challege挑戰發起者,下次繼續使用該憑證作為服務器的身份驗證。
    • 客戶端驗證: 將客服端憑證對象,通常都是由p12文件生成, 先將它轉換為CFData類型,再通過 valueForKey的方式,一層一層的將其撥開,取到最后的秘鑰,并生成NSURLCredential憑證對象。
    • AFNetworking框架最終生成的憑證對象,以及驗證方式,通過block的回調的方式在 delegate挑戰認證的方法中回調出去。
      + (void)configSSLChallage {
       AFHTTPSessionManager* mgr = [self shareInstance];
       __weak typeof(*&mgr)weakmgr = mgr;
       [mgr setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing  _Nullable * _Nullable credential) {
          NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
           __autoreleasing NSURLCredential *_credential =nil;
        
           //server 端驗證:
           if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            NSLog(@"authorMethod:%@",challenge.protectionSpace.authenticationMethod);
            if([weakmgr.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host] ) {
             
               _credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                if(credential) {
                    disposition =NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition =NSURLSessionAuthChallengePerformDefaultHandling;
                }
               } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
                }
            } else {
              //client 客戶端驗證:

                    NSLog(@"authorMethod:%@",challenge.protectionSpace.authenticationMethod);
            // client authentication
            SecIdentityRef identity = NULL;
            SecTrustRef trust = NULL;
            NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];
            NSFileManager *fileManager =[NSFileManager defaultManager];
            
            if(![fileManager fileExistsAtPath:p12])
            {
                NSLog(@"client.p12:not exist");
            }
            else
            {
                NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
                
                if ([PLMNetWorkTool extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
                {
                    if (extractIdentityAndTrust((__bridge CFDataRef)(PKCS12Data), &identity, &trust)== noErr) {
                        SecCertificateRef certificate = NULL;
                        SecIdentityCopyCertificate(identity, &certificate);
                        const void*certs[] = {certificate};
                        CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
                        
                        _credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge  NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
                        disposition =NSURLSessionAuthChallengeUseCredential;
                    }
                }
            }
         }
        *credential = _credential;
        return disposition;
         }];
 
        }

總結: https涉及的內容較多,如果不明白其中的具體原理,僅僅靠搬運一份代碼,那么一段出現問題將是非常致命的。理解其中的緣由對于我們應對https證書偽造,域名劫持,或是https會話秘鑰擴展,https安全通信雙層加固非常重要。

參考文獻: http://baike.baidu.com/link?url=QDGRYuHehLYwvUSNX_vH7xo8tQ8CJoDSSKeLP_PWzmohvk5E1E6RegmRGB0hERJW_0MqTBJn6sp6h65hLi_04a
http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容