Https安全通信(一)

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

https概要

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

https圖解

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


    Snip20161204_8.png

https通信詳解

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

https實踐

  • 在Apple Reference《Certificate,Key,and Trust Services Programming Guide》提供了https驗證的框架, security.該框架包含了 https通信中信任對象, 同時也提供https加密中SAH,MD5,RSA,AES等算法的支持。
  • 基于AFNetworking https單向驗證,系統(tǒng)框架已經(jīng)做了非常多的簡化步驟,在AFSecurity這個庫中,框架內(nèi)部已經(jīng)做好了相關(guān)的判定,比如根據(jù)是否為私有證書,將提供的 證書對象設(shè)置為錨證書,對服務(wù)器返回的 信任對象進(jìn)行評估,如果通過則進(jìn)行安全通信,反之則取消通信。
       + (AFSecurityPolicy*)configSecurityPolicy {
         NSString *cerPath = [[NSBundle mainBundle]                 pathForResource:@"https" ofType:@"cer"];//證書的路徑
         NSData *certData = [NSData dataWithContentsOfFile:cerPath];
         AFSecurityPolicy* securityPolicy = [AFSecurityPolicy    policyWithPinningMode:AFSSLPinningModeCertificate];
         securityPolicy.allowInvalidCertificates = YES;  /**如果采用三方頒發(fā)的證書,則不使用自建證書驗證服務(wù)器,由三方機構(gòu)驗證*/
         securityPolicy.validatesDomainName = YES;
         securityPolicy.pinnedCertificates  = [NSSet setWithObject:certData];
         mgr.securityPolicy = securityPolicy;
         return securityPolicy;
         }
  • 基于AFNetworking雙向通信。
    • 在AFNetworking中進(jìn)行雙向通信,需要我們自己對挑戰(zhàn)認(rèn)證進(jìn)行定義,其中服務(wù)器身份驗證部分,我們?nèi)匀豢梢匝赜蒙厦娴?security對象進(jìn)行自動校驗,但是客戶端驗證,就需要我們進(jìn)行實現(xiàn)了。
    • AFNetworking框架已經(jīng)為我們講調(diào)整驗證的方法是用回調(diào)Block抽離出來。在使用時候我們只需鑰注冊該回調(diào)block,在對應(yīng)的block中實現(xiàn)當(dāng)收到驗證服務(wù)器端憑證和需要提供客服端憑證的方法進(jìn)行處理.相關(guān)代碼實現(xiàn)方式如下.
    • 服務(wù)端驗證: 首先需要對挑戰(zhàn)challege對象的保護(hù)空間進(jìn)行判定,獲取當(dāng)前的驗證方式,如果是對服務(wù)器驗證,則取出serverTrust對象,并通過設(shè)定錨證書,與我們定義的服務(wù)器證書的信任對象進(jìn)行比對和評估,判定是否正確,如果正確,則告訴challege挑戰(zhàn)發(fā)起者,下次繼續(xù)使用該憑證作為服務(wù)器的身份驗證。
    • 客戶端驗證: 將客服端憑證對象,通常都是由p12文件生成, 先將它轉(zhuǎn)換為CFData類型,再通過 valueForKey的方式,一層一層的將其撥開,取到最后的秘鑰,并生成NSURLCredential憑證對象。
    • AFNetworking框架最終生成的憑證對象,以及驗證方式,通過block的回調(diào)的方式在 delegate挑戰(zhàn)認(rèn)證的方法中回調(diào)出去。
      + (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;
         }];
 
        }

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容