HTTPS從原理到應(yīng)用(四):iOS中HTTPS實(shí)際使用

前三篇加密和哈希瘪弓、數(shù)字簽名和數(shù)字證書HTTPS的核心SSL/TLS協(xié)議已經(jīng)把相關(guān)原理說完了房午,具體理解還要和實(shí)際使用結(jié)合起來。本人從事iOS開發(fā)竞膳,這里主要講述在iOS中的應(yīng)用。而在iOS中大部分網(wǎng)絡(luò)請求都是使用的AFNetworking這個(gè)第三方庫诫硕,而它又是基于NSURLSession的封裝坦辟,所以此文也會(huì)從這兩個(gè)方面進(jìn)行講解。由于NSURLConnection基本已經(jīng)無人使用章办,這個(gè)就不在提了锉走,大致使用和NSURLSession類似。

此篇文章的邏輯圖

圖0-0 此篇文章的邏輯圖

概述

無論是AFNetworking還是NSURLSession纲菌,整個(gè)HTTPS協(xié)議的傳輸挠日,都要經(jīng)過上文中提到的SSL/TLS的握手階段。創(chuàng)建一個(gè)請求翰舌,開始請求的時(shí)候嚣潜,開始第一個(gè)階段Client Hello,然后服務(wù)端Server Hello階段回應(yīng)客戶端椅贱,會(huì)到調(diào)用到NSURLSessionDelegate的兩個(gè)方法懂算,而客戶端在代理方法中處理SSL/TLS的第三個(gè)階段最后客戶端回應(yīng)服務(wù)端,然后服務(wù)端在驗(yàn)證庇麦,從建立起SSL/TLS的連接计技。而這四個(gè)過程中,服務(wù)端程序員主要操作對應(yīng)第二步山橄,客戶端程序員主要操作對應(yīng)第三步垮媒,而第三步里面主要操作的就是驗(yàn)證證書。其他的一些航棱,像產(chǎn)生隨機(jī)數(shù)等睡雇,并不需要程序員操作,相關(guān)底層都已經(jīng)封裝好了饮醇。根這過程下面分別介紹NSURLSessionAFNetworking是如何支持HTTPS的它抱。

NSURLSession支持HTTPS

更正:證書在系統(tǒng)默認(rèn)信任列表中,則可以直接按HTTP方式請求朴艰,一般不需要再寫下面的驗(yàn)證代碼观蓄。

證書在系統(tǒng)默認(rèn)信任列表中和不在列表中(自建證書)的驗(yàn)證方式不同混移,下面分別敘述。

證書在系統(tǒng)默認(rèn)信任列表中的驗(yàn)證

// 創(chuàng)建一個(gè)HTTPS請求侮穿,這步包括了SSL/TLS握手協(xié)議的第一步Client Hello
NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
[task resume];

NSURLSession對證書的認(rèn)證有兩個(gè)代理方法歌径,Server Hello階段后會(huì)來到這兩個(gè)代理方法中的其中一個(gè),SSL/TLS第三個(gè)階段主要就在這兒實(shí)現(xiàn)亲茅。下面以其中一個(gè)會(huì)話級別的回調(diào)為例說明沮脖。

// 會(huì)話級別的回調(diào)
- (void)URLSession:(NSURLSession *)session 
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler

// 任務(wù)級別的回調(diào)
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler

兩個(gè)代理方法的區(qū)別就是第二個(gè)回調(diào)多了一個(gè)task,
如果同時(shí)實(shí)現(xiàn)兩個(gè)代理方法芯急,則有回調(diào)優(yōu)先級別更高的會(huì)話代理方法。
- (void)URLSession:(NSURLSession *)session 
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler {

 NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;
    
    // 1. 先判斷服務(wù)器采用的認(rèn)證方法是否為NSURLAuthenticationMethodServerTrust驶俊,ServerTrust是比較常用的娶耍,當(dāng)然還有其他的認(rèn)證方法。
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        
        // 2. 獲取需要驗(yàn)證的信任對象饼酿,并采用系統(tǒng)默認(rèn)驗(yàn)證方式SecTrustEvaluate進(jìn)行驗(yàn)證榕酒,其中驗(yàn)證的API在Security庫中
        SecTrustRef trust = challenge.protectionSpace.serverTrust;
        SecTrustResultType result;
        OSStatus status = SecTrustEvaluate(trust, &result);
        
        if (status == errSecSuccess && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)) {
            // 3. 驗(yàn)證成功,根據(jù)服務(wù)器返回的受保護(hù)空間中的信任對象故俐,創(chuàng)建一個(gè)挑戰(zhàn)憑證想鹰,并且挑戰(zhàn)方式為使用憑證挑戰(zhàn)
            credential = [NSURLCredential credentialForTrust:trust];
            disposition = NSURLSessionAuthChallengeUseCredential;
            
        } else {
            // 3. 驗(yàn)證失敗,取消本次挑戰(zhàn)認(rèn)證
            disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
        }
    } else {
        // 1. 如果服務(wù)器采用的認(rèn)證方法不是ServerTrust药版,可判斷是否為其他認(rèn)證辑舷,如何NSURLAuthenticationMethodHTTPDigest,等等槽片,這里我沒有判斷何缓,直接處理為系統(tǒng)默認(rèn)處理。
        disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    }

    // 4. 無論結(jié)果如何还栓,都要回到給服務(wù)端
    completionHandler(disposition, credential);
}

自建證書的驗(yàn)證

由于自建證書并沒有在系統(tǒng)默認(rèn)的證書信任列表中碌廓,如果使用默認(rèn)驗(yàn)證方法是不會(huì)通過的,這時(shí)候就要App提前置入證書剩盒。
更新:但是如果ATSAllow Arbitrary Loads配置為NO谷婆,則無法通過驗(yàn)證。(2016-12-22)

// 先導(dǎo)入證書
// 證書的路徑
NSString *cerPath = ...;
NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
// 自建信任證書列表
self.trustedCertificates = @[CFBridgingRelease(certificate)];

- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler {
    
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;
    
    // 1. 先判斷服務(wù)器采用的認(rèn)證方法是否為NSURLAuthenticationMethodServerTrust辽聊,ServerTrust是比較常用的纪挎,當(dāng)然還有其他的認(rèn)證方法。
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        
        // 2. 獲取需要驗(yàn)證的信任對象身隐,并設(shè)置信任對象要驗(yàn)證的證書為之前導(dǎo)入的證書廷区,在SecTrustEvaluate進(jìn)行驗(yàn)證,其中驗(yàn)證的API在Security庫中
        SecTrustRef trust = challenge.protectionSpace.serverTrust;
        SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)self.trustedCertificates);
        SecTrustResultType result;
        OSStatus status = SecTrustEvaluate(trust, &result);
        
        if (status == errSecSuccess && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)) {
            // 3. 驗(yàn)證成功贾铝,根據(jù)服務(wù)器返回的受保護(hù)空間中的信任對象隙轻,創(chuàng)建一個(gè)挑戰(zhàn)憑證埠帕,并且挑戰(zhàn)方式為使用憑證挑戰(zhàn)
            credential = [NSURLCredential credentialForTrust:trust];
            disposition = NSURLSessionAuthChallengeUseCredential;
            
        } else {
            // 3. 驗(yàn)證失敗,取消本次挑戰(zhàn)認(rèn)證
            disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
        }
    } else {
        // 1. 如果服務(wù)器采用的認(rèn)證方法不是ServerTrust玖绿,可判斷是否為其他認(rèn)證敛瓷,如何NSURLAuthenticationMethodHTTPDigest,等等斑匪,這里我沒有判斷呐籽,直接處理為系統(tǒng)默認(rèn)處理。
        disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    }

    // 4. 無論結(jié)果如何蚀瘸,都要回到給服務(wù)端
    completionHandler(disposition, credential);
}

AFNetworking支持HTTPS

概述

AFNetworking支持HTTPS相關(guān)的類為AFSecurityPolicy狡蝶,其中AFNetworking提供了三種驗(yàn)證方式:

typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { 
  AFSSLPinningModeNone,        // 是默認(rèn)的認(rèn)證方式,只會(huì)在系統(tǒng)的信任的證書列表中對服務(wù)端返回的證書進(jìn)行驗(yàn)證
  AFSSLPinningModePublicKey,   // 需要預(yù)先保存服務(wù)端發(fā)送的證書(自建證書)贮勃,但是這里只會(huì)驗(yàn)證證書中的公鑰是否正確
  AFSSLPinningModeCertificate, // 需要客戶端預(yù)先保存服務(wù)端的證書(自建證書)
};
// AFSecurityPolicy相關(guān)屬性

// 驗(yàn)證方式贪惹,對應(yīng)上面的三種驗(yàn)證方式
@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
// 自建證書的時(shí)候,提供相應(yīng)的證書
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
// 是否允許自建證書
@property (nonatomic, assign) BOOL allowInvalidCertificates;
// 是否需要驗(yàn)證域名
@property (nonatomic, assign) BOOL validatesDomainName;

--------------------------------------

// AFSecurityPolicy相關(guān)方法

// 驗(yàn)證邏輯寂嘉,這個(gè)方法在AFURLSessionManager這個(gè)類中奏瞬,實(shí)現(xiàn)的NSURLSession的兩個(gè)代理方法中調(diào)用。
// 具體驗(yàn)證邏輯泉孩,大家可讀AFNetworking源碼
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(nullable NSString *)domain;

實(shí)際運(yùn)用


AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
policy.allowInvalidCertificates = YES;
policy.validatesDomainName = YES;
// 證書的路徑
NSString *cerPath = ...;
NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
policy.pinnedCertificates = [NSSet setWithObject:CFBridgingRelease(certificate)];
manager.securityPolicy = policy;
// 下面使用manager開始請求硼端,由于AFNetworking的高度封裝,使用起來及其方便寓搬。

總結(jié)

關(guān)于AFNetworking并沒有提及太多珍昨,相關(guān)部分可讀一下源碼,看一下此文并沒有提到的自建證書公鑰驗(yàn)證句喷,其實(shí)就是從證書中提取出來公鑰曼尊。另外也可大致瀏覽一下Security這個(gè)Framework,你會(huì)發(fā)現(xiàn)不少原理中提到的知識脏嚷,比如<Security/CipherSuite.h>里面的加密組件骆撇,特別是<Security/SecureTransport.h>中定義的OSStatus狀態(tài)碼非常有助于調(diào)試,還有SSL/TLS版本父叙,以及文章中從沒有提過的X.509標(biāo)準(zhǔn)神郊。到此HTTPS的基本原理和使用交代完畢,讀完此系列文章趾唱,會(huì)對HTTPS有個(gè)詳細(xì)的了解涌乳。但是HTTPS也不是絕對安全的,還是會(huì)有很多的攻擊方法甜癞。有時(shí)間會(huì)再出一篇文章來專門講解HTTPS的攻防夕晓。文章從最基礎(chǔ)的講起,分了四篇來寫悠咱,也是比較長的蒸辆;感謝大家耐心看完征炼,對于文章不足之處,敬請包含躬贡,也請多提意見谆奥,共同進(jìn)步。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拂玻,一起剝皮案震驚了整個(gè)濱河市酸些,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌檐蚜,老刑警劉巖魄懂,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異闯第,居然都是意外死亡逢渔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門乡括,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人智厌,你說我怎么就攤上這事诲泌。” “怎么了铣鹏?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵敷扫,是天一觀的道長。 經(jīng)常有香客問我诚卸,道長葵第,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任合溺,我火速辦了婚禮卒密,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘棠赛。我一直安慰自己哮奇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布睛约。 她就那樣靜靜地躺著鼎俘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辩涝。 梳的紋絲不亂的頭發(fā)上贸伐,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音怔揩,去河邊找鬼捉邢。 笑死脯丝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的歌逢。 我是一名探鬼主播巾钉,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼秘案!你這毒婦竟也來了砰苍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤阱高,失蹤者是張志新(化名)和其女友劉穎赚导,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赤惊,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吼旧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了未舟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片圈暗。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖裕膀,靈堂內(nèi)的尸體忽然破棺而出员串,到底是詐尸還是另有隱情,我是刑警寧澤昼扛,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布寸齐,位于F島的核電站,受9級特大地震影響抄谐,放射性物質(zhì)發(fā)生泄漏渺鹦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一蛹含、第九天 我趴在偏房一處隱蔽的房頂上張望毅厚。 院中可真熱鬧,春花似錦浦箱、人聲如沸卧斟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽珍语。三九已至,卻和暖如春竖幔,著一層夾襖步出監(jiān)牢的瞬間板乙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留募逞,地道東北人蛋铆。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像放接,于是被迫代替她去往敵國和親刺啦。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 原文地址 http://blog.csdn.net/u012409247/article/details/4985...
    0fbf551ff6fb閱讀 3,521評論 0 13
  • 原文地址:iOS安全系列之一:HTTPS 如何打造一個(gè)安全的App纠脾?這是每一個(gè)移動(dòng)開發(fā)者必須面對的問題玛瘸。在移動(dòng)Ap...
    violafa閱讀 870評論 0 2
  • 一、作用 不使用SSL/TLS的HTTP通信苟蹈,就是不加密的通信糊渊。所有信息明文傳播,帶來了三大風(fēng)險(xiǎn)慧脱。 (1)竊聽風(fēng)險(xiǎn)...
    XLsn0w閱讀 10,527評論 2 44
  • 本想在這篇文章中單獨(dú)寫AFNetworking 3.0中AFSecurityPolicy的源碼閱讀筆記的渺绒。但隨著源...
    WeiHing閱讀 2,564評論 1 13
  • 這篇文章是我一邊學(xué)習(xí)證書驗(yàn)證一邊記錄的內(nèi)容,稍微整理了下菱鸥,共扯了三部分內(nèi)容: HTTPS 簡要原理宗兼;數(shù)字證書的內(nèi)容...
    左邊飛來一只狗閱讀 3,292評論 2 5