AFNetworking 3.0 源碼閱讀-HTTPS認(rèn)證-AFSecurityPolicy

本想在這篇文章中單獨寫AFNetworking 3.0中AFSecurityPolicy的源碼閱讀筆記的吏颖。但隨著源碼閱讀的過程馅闽,發(fā)現(xiàn)關(guān)于有太多相關(guān)背景知識需要惡補..所以一邊學(xué)習(xí)一邊總結(jié)寫了這篇文章表谊。如果有寫錯的地方绷雏,請及時指正紊遵。

1.HTTPS

HTTPS 是運行在 TLS/SSL 之上的 HTTP窄绒,是為了解決HTTP通信不安全的問題而設(shè)計的。

  • 對稱加密成洗、非對稱加密
    對稱加密使用同一個密鑰進(jìn)行加密解密五督,速度快。
    非對稱加密使用公鑰加密瓶殃,私鑰解密充包,計算量大速度慢。非對稱加密又稱公鑰密碼技術(shù)

使用時兩者折中遥椿。在SSL/TLS中基矮,用“對稱加密”來加解密通信信息,速度快冠场;使用“非對稱加密”來加解密“對稱密鑰”家浇。

  • SSL/TLS四次握手


    SSL/TLS協(xié)議運行機制的概述-阮一峰

    1.客戶端發(fā)出請求

  • ClientHello 向服務(wù)器傳遞隨機數(shù)1、協(xié)議版本碴裙、客戶端支持的加密套件(Cipher Suites)钢悲、壓縮方法、簽名算法等信息青团。

2.服務(wù)器回應(yīng)

  • ** SeverHello** 服務(wù)器收到客戶端請求后譬巫,向客戶端發(fā)出回應(yīng)。傳遞內(nèi)容:確認(rèn)使用的協(xié)議版本督笆、從收到的客戶端加密套件中篩選出來的加密方法芦昔、壓縮算法和簽名算法,和服務(wù)器新生成的一個隨機數(shù)2等等返回給客戶端娃肿。
  • severCertificate 服務(wù)器發(fā)送數(shù)字證書(其實就是要拿到公鑰)咕缎。
  • CertificateRequest 如果服務(wù)器需要確認(rèn)客戶端的身份(雙向認(rèn)證),就會再包含一項請求料扰,要求客戶端提供"客戶端證書"凭豪。比如,金融機構(gòu)往往只允許認(rèn)證客戶連入自己的網(wǎng)絡(luò)晒杈,就會向正式客戶提供USB密鑰嫂伞,里面就包含了一張客戶端證書。

3.客戶端回應(yīng)

  • Client Key Exchange 客戶端確認(rèn)證書有效拯钻,則會生產(chǎn)最后一個隨機數(shù)3(pre-master key)帖努,并使用證書的公鑰加密這個隨機數(shù),發(fā)回給服務(wù)端粪般。(為了更高的安全性拼余,會使用Diffie-Hellman算法;采用DH算法亩歹,最后一個隨機數(shù)是不需要傳遞的匙监,客戶端和服務(wù)端交換參數(shù)之后就可以算出)
  • Change Cipher Spec 通知對方凡橱,編碼改變,接下來的所有消息都會使用雙方商定的加密方法和密鑰發(fā)送亭姥。
  • Finished 客戶端握手結(jié)束通知稼钩,表示客戶端的握手階段已經(jīng)結(jié)束。該報文包含前面發(fā)送的所有報文的整體校驗值(hash)致份,用來供服務(wù)器校驗变抽。

4.服務(wù)器回應(yīng)

  • Change Cipher Spec 服務(wù)端同樣發(fā)送Change Cipher Spec報文。
  • Finished 服務(wù)端同樣發(fā)送Finished報文

至此氮块,整個握手階段全部結(jié)束。接下來诡宗,客戶端與服務(wù)器進(jìn)入加密通信滔蝉,就完全是使用普通的HTTP協(xié)議,只不過用"會話密鑰"加密內(nèi)容塔沃。
至于這個會話密鑰呢蝠引,就是通信兩端同時擁有的三個隨機數(shù),用雙方事先商定的加密方法蛀柴,各自生成之后通信所用的對稱密鑰螃概。

  • 簡單總結(jié)一下,這四次握手過程主要交換了:
    1.證書鸽疾,一般由服務(wù)器發(fā)給客戶端吊洼;驗證證書是不是可信機構(gòu)頒發(fā)的,如果是自簽證書制肮,一般在客戶端本地置入證書拷貝冒窍,然后兩份證書對比來判斷證書是否可信。
    如果是雙向認(rèn)證的豺鼻,客戶端也要給服務(wù)器發(fā)送證書综液。關(guān)于單向、雙向認(rèn)證

2.三個隨機數(shù)儒飒,用來生成后續(xù)通信進(jìn)行加解密的對稱密鑰谬莹。其中前兩個隨機數(shù)都是明文傳輸,只有第三個隨機數(shù)是加密的(公鑰足夠長桩了,2048位附帽,可保證不被破解)。
為什么是三個隨機數(shù)圣猎?SSL協(xié)議不相信每個主機都能產(chǎn)生完全隨機的隨機數(shù)士葫,如果只有一個偽隨機數(shù)就容易被破解,如果3個偽隨機數(shù)就接近隨機了送悔。

3.加密方式

  • 其他慢显,session恢復(fù)
    由于新建立一個SSL/TLS Session的成本太高爪模,所以之前有建立SSL/TLS連接Session的話,客戶端會保存Session ID荚藻。如果對話中斷屋灌,在下一次請求時在Client Hello中帶上session ID,服務(wù)端驗證有效之后应狱,就會成功重用Sesssion共郭。雙方就不再進(jìn)行握手階段剩余的步驟,而直接用已有的對話密鑰進(jìn)行加密通信

2.數(shù)字證書

這里先簡單講一些數(shù)字簽名疾呻。它能確認(rèn)消息的完整性除嘹,進(jìn)行認(rèn)證。公鑰密碼一樣也要用到一對公鑰岸蜗、私鑰尉咕。但簽名是用私鑰加密(生成簽名),公鑰解密(驗證簽名)璃岳。私鑰加密只能由持有私鑰的人完成年缎,而由于公鑰是對外公開的,因此任何人都可以用公鑰進(jìn)行解密(驗證簽名)铃慷。

要確認(rèn)公鑰是否合法单芜,就需要使用證書。

公鑰證書一般會記有姓名犁柜、組織洲鸠、郵箱地址等個人信息,以及屬于本人的公鑰赁温,并由認(rèn)證機構(gòu)(CA)進(jìn)行數(shù)字簽名坛怪。通過認(rèn)證機構(gòu)使用證書的過程如下圖所示:


《圖解密碼技術(shù)》

公鑰基礎(chǔ)設(shè)施(PKI)是為了能夠更有效地運用公鑰而制定的一系列規(guī)范和規(guī)格的總稱。使用最廣泛的 X.509 規(guī)范也是PKI的一種股囊。

  • 證書鏈
    CA有層級的關(guān)系袜匿,處于最頂層的認(rèn)證機構(gòu)一般就稱為根CA。下層證書是經(jīng)過上層簽名的稚疹。而根CA則會對自己的證書進(jìn)行簽名居灯,即自簽名

怎么驗證證書有沒有被篡改内狗?
當(dāng)客戶端走 HTTPS 訪問站點時怪嫌,服務(wù)器會返回整個證書鏈。先從最底層的CA開始柳沙,用上層的公鑰對下層證書的數(shù)字簽名進(jìn)行驗證岩灭。這樣逐層向上驗證,直到遇到了錨點證書赂鲤。

  • 錨點證書
    嵌入到操作系統(tǒng)中的根證書(系統(tǒng)隱式信任的證書)噪径,通常是包括在系統(tǒng)中的 CA 根證書柱恤。不過你也可以在驗證證書鏈時,設(shè)置自定義的證書作為可信的錨點找爱。

3.SSL Pinning

HTTPS挺安全的但也不是無懈可擊梗顺。本人在網(wǎng)絡(luò)安全方面也不是專業(yè)的,這里就簡單說一點车摄。中間人攻擊寺谤。簡單來說,iPhone信任的證書包括一些預(yù)裝的證書和用戶自己安裝的證書吮播,如果攻擊者手上擁有一個受信任的證書变屁,那么就會發(fā)生中間人攻擊了。
這時候就需要SSL Pinning 了意狠,原理是把server證書的拷貝捆綁在APP中敞贡,client通過對比server發(fā)來的證書檢測它有沒有被篡改。結(jié)合這篇文章中講的SSL中間人攻擊摄职、模擬攻擊實例,對上面所講的有更好的理解获列。

4.證書校驗

4.1域名驗證

服務(wù)器證書上的域名和請求域名是否匹配谷市。
使用NSURLSession獲取默認(rèn)驗證策略:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    CFArrayRef defaultPolicies = NULL;//獲取默認(rèn)的校驗策略
    SecTrustCopyPolicies(trust, &defaultPolicies);
    NSLog(@"Default Trust Policies: %@", (__bridge id)defaultPolicies);
}

默認(rèn)的驗證策略是包含域名驗證的。如果想重置驗證策略击孩,可以調(diào)用SecTrustSetPolicies迫悠。比如AFNetworking中就是這樣做的:

NSMutableArray *policies = [NSMutableArray array];
//BasicX509 就是不驗證域名,返回的服務(wù)器證書巩梢,只要是可信任CA機構(gòu)簽發(fā)的创泄,都會校驗通過
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];    
//設(shè)置評估中要使用的策略
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);

4.2校驗證書鏈

證書鏈的驗證,主要由三部分來保證證書的可信:葉子證書是對應(yīng)HTTPS請求域名的證書括蝠,根證書是被系統(tǒng)信任的證書鞠抑,以及這個證書鏈之間都是層層簽發(fā)可信任鏈;證書之所以能成立忌警,本質(zhì)是基于信任鏈搁拙,這樣任何一個節(jié)點證書加上域名校驗(CA機構(gòu)不會為不同的對不同的用戶簽發(fā)相同域名的證書),就確定一條唯一可信證書鏈法绵。

基于證書信任鏈進(jìn)行校驗箕速。如果該信任鏈只包含有效的證書并以已知的錨證書結(jié)束,那么證書被認(rèn)為是有效的(當(dāng)返回的服務(wù)器證書是錨點證書或者是基于該證書簽發(fā)的證書(可以是多個層級)都會被信任)朋譬。這里的錨證書也可以是自定義的證書盐茎,使用SecTrustSetAnchorCertificates函數(shù)設(shè)置錨點證書。比如AFNetworking中:

NSMutableArray *pinnedCertificates = [NSMutableArray array];
//把nsdata證書(der編碼的x.509證書)轉(zhuǎn)成SecCertificateRef類型的數(shù)據(jù)
for (NSData *certificateData in self.pinnedCertificates) {
  [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
//將本地證書設(shè)置成需要參與驗證的錨點證書
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
//驗證服務(wù)器證書是否可信(由系統(tǒng)默認(rèn)可信或者由用戶選擇可信)徙赢。
if (!AFServerTrustIsValid(serverTrust)) {
  return NO;
}

ps:只使用SecTrustSetAnchorCertificates函數(shù)字柠,沒使用SecTrustSetAnchorCertificatesOnly探越,就只會相信SecTrustSetAnchorCertificates由該錨點證書頒發(fā)的證書,哪怕是由系統(tǒng)其他錨點證書頒發(fā)的其他證書也不會通過驗證募谎。
如果要想恢復(fù)系統(tǒng)中 CA 證書作為錨點的功能:

// true 代表僅被傳入的證書作為錨點扶关,false 允許系統(tǒng) CA 證書也作為錨點
SecTrustSetAnchorCertificatesOnly(trust, false);

4.3SSL Pinning把證書打包進(jìn)app

如果用戶訪問不安全鏈接并且選擇信任了不該信任的證書,證書校驗依賴的源受污染数冬,因此不能只依賴證書鏈來進(jìn)行證書校驗节槐。安全的做法是,把證書拷貝打包進(jìn)app中并把它作為錨點證書拐纱,然后和服務(wù)器的證書鏈做匹配铜异。
比如在AFNetworking中:

SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
//驗證服務(wù)器證書是否可信(由系統(tǒng)默認(rèn)可信或者由用戶選擇可信)。
if (!AFServerTrustIsValid(serverTrust)) {
    return NO;
}
//從我們需要被驗證的服務(wù)端去拿證書鏈
//這里的證書鏈順序是從葉節(jié)點到根節(jié)點
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
//逆序秸架,從根節(jié)點開始匹配            
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
     //如果本地證書中揍庄,有一個和它證書鏈中的證書匹配的,就返回YES
    if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
        return YES;
    }
}

5.AFNetworking3.0版本中的HTTPS認(rèn)證

自 iOS 9 發(fā)布之后东抹,由于新特性App Transport Security的引入蚂子,默認(rèn)情況下是不能發(fā)送 HTTP 請求的。很多網(wǎng)站都在轉(zhuǎn)用 HTTPS缭黔,而 AFNetworking中的 AFSecurityPolicy就是用來滿足我們各種https認(rèn)證需求的食茎。
接下來從源碼入手分析AFSecurityPolicy內(nèi)部是如何做https認(rèn)證的。(AF默認(rèn)做的僅僅是單向認(rèn)證馏谨,如果要做雙向認(rèn)證就只能自己寫block來實現(xiàn)了)

5.1AFSSLPinningMode和重要屬性

  1. AFSSLPinningMode共提供了三種驗證方式
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {//三種驗證服務(wù)器的方式
    AFSSLPinningModeNone,//不使用固定證書(本地)驗證服務(wù)器别渔。直接從客戶端系統(tǒng)中的受信任頒發(fā)機構(gòu) CA 列表中去驗證
    AFSSLPinningModePublicKey,//根據(jù)本地固定證書公鑰驗證服務(wù)器證書,不驗證證書的有效期等信息
    AFSSLPinningModeCertificate,//根據(jù)本地固定證書驗證服務(wù)器證書
};

AFSSLPinningModeNone不做本地證書驗證惧互,直接從客戶端系統(tǒng)中的受信任頒發(fā)機構(gòu) CA 列表中去驗證服務(wù)端返回的證書哎媚,若證書是信任機構(gòu)簽發(fā)的就通過,若是自己服務(wù)器生成的證書喊儡,這里是不會通過的拨与。
AFSSLPinningModePublicKey用ssl pinning方式驗證服務(wù)端返回的證書,只驗證公鑰管宵〗卣洌客戶端要有服務(wù)端證書拷貝
AFSSLPinningModeCertificate用ssl pinning方式驗證服務(wù)端返回的證書÷崞樱客戶端要有服務(wù)端證書拷貝

  1. AFSecurityPolicy中重要的屬性
//ssl pinning的模式岗喉,默認(rèn)是AFSSLPinningModeNone
@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
//本地證書,用于驗證服務(wù)器
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
//是否信任無效或者過期的ssl證書炸庞,默認(rèn)不信任(比如自簽名證書)
@property (nonatomic, assign) BOOL allowInvalidCertificates;
//是否驗證證書域名钱床,默認(rèn)是YES
@property (nonatomic, assign) BOOL validatesDomainName;
//本地證書公鑰
@property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys;

5.2初始化及設(shè)置

1.初始化

//創(chuàng)建一個默認(rèn)的AFSecurityPolicy,SSLPinningMode是不驗證
+ (instancetype)defaultPolicy {
    AFSecurityPolicy *securityPolicy = [[self alloc] init];
    securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
    return securityPolicy;
}

+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
    return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
}

//根據(jù)指定的驗證模式埠居、證書創(chuàng)建AFSecurityPolicy
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
    AFSecurityPolicy *securityPolicy = [[self alloc] init];
    securityPolicy.SSLPinningMode = pinningMode;
    [securityPolicy setPinnedCertificates:pinnedCertificates];
    return securityPolicy;
}

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }
    self.validatesDomainName = YES;//驗證證書域名
    return self;
}

這里沒有什么地方值得解釋的查牌,根據(jù)需要選擇創(chuàng)建一個默認(rèn)的AFSecurityPolicy事期,或者根據(jù)指定的AFSSLPinningMode、PinnedCertificates創(chuàng)建AFSecurityPolicy纸颜。
在AF中是這樣創(chuàng)建一個securityPolicy的:self.securityPolicy = [AFSecurityPolicy defaultPolicy];

2.設(shè)置本地證書PinnedCertificates
在調(diào)用- setPinnedCertificates:方法設(shè)置本地證書時兽泣,會把全部證書的公鑰取出來存放到pinnedPublicKeys屬性中,方便之后用于AFSSLPinningModePublicKey方式的驗證

//設(shè)置用于評估服務(wù)器是否可信的證書(本地證書)
//把證書中每個公鑰放在了self.pinnedPublicKeys中,用于AFSSLPinningModePublicKey方式的驗證
- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
    _pinnedCertificates = pinnedCertificates;

    if (self.pinnedCertificates) {
        //遍歷取出所有證書中的公鑰胁孙,然后保存在self.pinnedPublicKeys屬性中
        NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
        for (NSData *certificate in self.pinnedCertificates) {
            id publicKey = AFPublicKeyForCertificate(certificate);//從證書中獲取公鑰
            if (!publicKey) {
                continue;
            }
            [mutablePinnedPublicKeys addObject:publicKey];
        }
        self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
    } else {
        self.pinnedPublicKeys = nil;
    }
}

3.其他

//以NSData的形式獲取某個目錄下的所有證書
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle;
//以NSData的形式獲取當(dāng)前class目錄下的所有證書
+ (NSSet *)defaultPinnedCertificates;

5.3核心方法

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain
這個方法可以說是這個類的核心了唠倦。用于驗證服務(wù)器是否可信。
這個方法有兩個參數(shù):SecTrustRef類型serverTrustNSString類型的domain

  • SecTrustRef是啥
    在這個方法中涮较,這個serverTrust是服務(wù)器傳過來的稠鼻,包含了服務(wù)器的證書信息。
    大概是用來執(zhí)行X.509證書信任評估的狂票。

其實就是一個容器候齿,裝了服務(wù)器端需要驗證的證書的基本信息、公鑰等等闺属,不僅如此慌盯,它還可以裝一些評估策略,還有客戶端的錨點證書掂器,這個客戶端的證書润匙,可以用來和服務(wù)端的證書去匹配驗證的。
每一個SecTrustRef對象包含多個SecCertificateRef 和 SecPolicyRef唉匾。其中 SecCertificateRef 可以使用 DER 進(jìn)行表示。

  • domain服務(wù)器域名匠楚,用于域名驗證

代碼解析如下:

//驗證服務(wù)端是否可信巍膘,這個serverTrust是服務(wù)器傳過來的,里面包含了服務(wù)器的證書信息芋簿,是用于我們本地客戶端去驗證該證書是否合法用的
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(NSString *)domain
{
    //判斷矛盾的條件
    //如果有服務(wù)器域名峡懈、設(shè)置了允許信任無效或者過期證書(自簽名證書)、需要驗證域名与斤、沒有提供證書或者不驗證證書肪康,返回no猪狈。后兩者和allowInvalidCertificates為真的設(shè)置矛盾冯吓,說明這次驗證是不安全的。
    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
        // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
        //  According to the docs, you should only trust your provided certs for evaluation.
        //  Pinned certificates are added to the trust. Without pinned certificates,
        //  there is nothing to evaluate against.
        //
        //  From Apple Docs:
        //          "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
        //           Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
        NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
        return NO;
    }

    //生成驗證策略楼咳。如果要驗證域名食寡,就以域名為參數(shù)創(chuàng)建一個策略雾狈,否則創(chuàng)建默認(rèn)的basicX509策略
    NSMutableArray *policies = [NSMutableArray array];
    if (self.validatesDomainName) {
        //SecPolicyCreateSSL函數(shù),創(chuàng)建用于評估SSL證書鏈的策略對象抵皱。第一個參數(shù):true將為SSL服務(wù)器證書創(chuàng)建一個策略善榛。第二個參數(shù):如果這個參數(shù)存在辩蛋,證書鏈上的葉子節(jié)點表示的那個domain要和傳入的domain相匹配
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];//該策略不檢驗域名
    }
    
    //設(shè)置評估中要使用的策略
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);//為serverTrust設(shè)置驗證的策略

    //如果是AFSSLPinningModeNone(不做本地證書驗證,從客戶端系統(tǒng)中的受信任頒發(fā)機構(gòu) CA 列表中去驗證)
    if (self.SSLPinningMode == AFSSLPinningModeNone) {
        //不使用ssl pinning 但允許自建證書移盆,直接返回YES悼院;否則進(jìn)行第二個條件判斷,去客戶端系統(tǒng)根證書里找是否有匹配的證書咒循,驗證serverTrust是否可信据途,直接返回YES;否則進(jìn)行第二個條件判斷剑鞍,去客戶端系統(tǒng)根證書里找是否有匹配的證書昨凡,驗證serverTrust是否可信
        return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
    }
    //如果serverTrust不可信且不允許自簽名,返回NO
    else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
        return NO;
    }

    //根據(jù)不同的SSLPinningMode分情況驗證
    switch (self.SSLPinningMode) {
        //不驗證
        case AFSSLPinningModeNone://上一部分已經(jīng)判斷過了蚁署,如果執(zhí)行到這里的話就返回NO
        default:
            return NO;
        //驗證證書
        case AFSSLPinningModeCertificate: {
            NSMutableArray *pinnedCertificates = [NSMutableArray array];
            //把nsdata證書(der編碼的x.509證書)轉(zhuǎn)成SecCertificateRef類型的數(shù)據(jù)
            for (NSData *certificateData in self.pinnedCertificates) {
                [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
            }
            // 將本地證書設(shè)置成需要參與驗證的錨點證書便脊,設(shè)為服務(wù)器信任的證書(錨點證書通常指:嵌入到操作系統(tǒng)中的根證書,通過SecTrustSetAnchorCertificates設(shè)置了參與校驗錨點證書之后光戈,假如驗證的數(shù)字證書是這個錨點證書的子節(jié)點哪痰,即驗證的數(shù)字證書是由錨點證書對應(yīng)CA或子CA簽發(fā)的,或是該證書本身久妆,則信任該證書)
            //第二個參數(shù)晌杰,表示在驗證證書時被SecTrustEvaluate函數(shù)視為有效(可信任)錨點的錨定證書集。 傳遞NULL以恢復(fù)默認(rèn)的錨證書集肋演。
            //自簽證書在這步之前驗證通過不了,把本地證書添加進(jìn)去后就能驗證成功烂琴。
            SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
            //驗證服務(wù)器證書是否可信爹殊。
            if (!AFServerTrustIsValid(serverTrust)) {
                return NO;
            }

            // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
            //從我們需要被驗證的服務(wù)端去拿證書鏈
            //這里的證書鏈順序是從葉節(jié)點到根節(jié)點
            NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
            //服務(wù)端證書鏈從根節(jié)點往葉節(jié)點遍歷
            for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {//reverseObjectEnumerator逆序
                //如果本地證書中,有一個和它證書鏈中的證書匹配的奸绷,就返回YES
                if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                    return YES;
                }
            }
            
            return NO;
        }
        //公鑰驗證 客戶端有服務(wù)端的證書拷貝梗夸,只要公鑰是正確的,就能保證通信不會被竊聽号醉,因為中間人沒有私鑰反症,無法解開通過公鑰加密的數(shù)據(jù)。
        case AFSSLPinningModePublicKey: {
            NSUInteger trustedPublicKeyCount = 0;
            // 從serverTrust中取出服務(wù)器端傳過來的所有可用的證書畔派,并依次得到相應(yīng)的公鑰
            NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
            //和本地公鑰遍歷對比
            for (id trustChainPublicKey in publicKeys) {
                for (id pinnedPublicKey in self.pinnedPublicKeys) {
                    if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                        trustedPublicKeyCount += 1;//判斷如果相同 trustedPublicKeyCount+1
                    }
                }
            }
            return trustedPublicKeyCount > 0;
        }
    }
    
    return NO;
}

總結(jié)一下這個方法做了什么
1.判斷設(shè)置上的矛盾條件铅碍。
允許使用自建證書self.allowInvalidCertificates=YES,還想驗證域名是否有效self.validatesDomainName=YES线椰,那么必須使用SSL Pinning方式驗證该酗,但AFSSLPinningModeNone表示不使用SSL Pinning。再者,如果沒有pinnedCertificates(在客戶端保存的服務(wù)器頒發(fā)的證書拷貝呜魄,在下文稱為“本地證書”)悔叽,表示無法驗證自建證書。

2.創(chuàng)建證書評估策略
如果要驗證域名爵嗅,就創(chuàng)建評估SSL證書鏈的策略娇澎;如果不驗證域名,就使用默認(rèn)的X509驗證策略睹晒。

3.設(shè)置評估策略

4.(在還沒設(shè)置本地錨點證書下趟庄,做第一次服務(wù)器驗證)
4.1如果是AFSSLPinningModeNone,不使用ssl pinning 但允許自建證書伪很,直接返回YES戚啥;或者使用SecTrustEvaluate去客戶端系統(tǒng)根證書里找是否有匹配的證書猫十,驗證serverTrust是否可信拖云。
4.2serverTrust不可信且不允許自建證書宙项,返回NO。

5.根據(jù)不同的SSL Pinning Mode驗證
5.1 AFSSLPinningModeNone
直接返回NO洞就,因為前面的處理應(yīng)該可以解決這種情況了咖为。
5.2 AFSSLPinningModeCertificate
將本地這書設(shè)置成錨點證書躁染,然后調(diào)用SecTrustEvaluate驗證服務(wù)端證書是否可信。拿到服務(wù)端證書鏈饰恕,如果本地證書中有一個和它證書鏈中的證書匹配的(相當(dāng)于是認(rèn)為服務(wù)端證書在本地信任的證書列表中埋嵌?)就返回YES雹嗦。
假設(shè)是自簽證書了罪,因為APP bundle中的證書不是CA機構(gòu)頒發(fā)的辅辩,不被信任汽久。要調(diào)用SecTrustSetAnchorCertificates將本地證書設(shè)置成serverTrust證書鏈上的錨點證書(好比于將這些證書設(shè)置成系統(tǒng)信任的根證書)景醇,然后調(diào)用SecTrustEvaluate校驗三痰,如果遇到錨點證書就終止校驗了散劫。
5.3 AFSSLPinningModePublicKey
取出服務(wù)端證書公鑰,和本地證書公鑰進(jìn)行匹配失乾。

這個核心方法中用到一些私有函數(shù)裸卫,這里簡單講一下墓贿。
1.AFPublicKeyForCertificate聋袋、AFServerTrustIsValid舱馅、AFCertificateTrustChainForServerTrust
這三個函數(shù)的實現(xiàn)比較相似代嗤,這里放一起講宜猜。

//在證書中獲取公鑰
static id AFPublicKeyForCertificate(NSData *certificate) {
    //1.初始化臨時變量
    id allowedPublicKey = nil;
    SecCertificateRef allowedCertificate;//SecCertificateRef包含有關(guān)證書的信息
    SecPolicyRef policy = nil;
    SecTrustRef allowedTrust = nil;
    SecTrustResultType result;

    //2.創(chuàng)建SecCertificateRef對象姨拥,判斷返回值是否為null
    //通過DER格式的證書(NSData)生成SecCertificateRef類型的證書引用叫乌。 如果傳入的數(shù)據(jù)不是有效的DER編碼的X.509證書憨奸,則返回NULL。
    //傳入的第二個參數(shù)是CFDataRef類型那婉,要用__bridge把oc對象轉(zhuǎn)Core Foundation對象
    allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
    //__Require_Quiet這個宏盐类,判斷allowedCertificate != NULL表達(dá)式是否為假在跳,如果allowedCertificate=NULL,就跳到_out標(biāo)簽處執(zhí)行
    __Require_Quiet(allowedCertificate != NULL, _out);

    //3.1.新建默認(rèn)策略為X.509的SecPolicyRef策略對象
    policy = SecPolicyCreateBasicX509();
    /*3.2.
     OSStatus SecTrustCreateWithCertificates(CFTypeRef certificates,
     CFTypeRef __nullable policies, SecTrustRef * __nonnull CF_RETURNS_RETAINED trust)
     基于給定的證書和策略創(chuàng)建一個SecTrustRef信任引用對象礼仗,然后賦值給trust韭脊。
     這個函數(shù)返回一個結(jié)果碼沪羔,判斷是否出錯
     */
    //__Require_noErr_Quiet蔫饰,第一個參數(shù)是錯誤碼表達(dá)式篓吁,如果不等于0(出錯了)就跳到_out標(biāo)簽處執(zhí)行
    __Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);//創(chuàng)建SecTrustRef,如果出錯就跳到_out
    //3.3對SecTrustRef進(jìn)行信任評估盛嘿,確認(rèn)它是值得信任的
    __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);

    //4.獲取證書公鑰
    //__bridge_transfer會將結(jié)果橋接成OC對象次兆,然后將 SecTrustCopyPublicKey 返回的指針釋放
    allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);

_out:
    //5.釋放c指針
    if (allowedTrust) {
        CFRelease(allowedTrust);
    }

    if (policy) {
        CFRelease(policy);
    }

    if (allowedCertificate) {
        CFRelease(allowedCertificate);
    }

    return allowedPublicKey;
}

這里用到的系統(tǒng)宏__Require_Quiet类垦,是用來判斷allowedCertificate != NULL表達(dá)式是否為假蚤认,如果allowedCertificate=NULL,就跳到_out標(biāo)簽處執(zhí)行代碼陪汽。

AFPublicKeyTrustChainForServerTrust函數(shù)的實現(xiàn)和它差不多挚冤,這里就不具體展開了训挡,用于取出服務(wù)器返回的證書鏈的每個證書公鑰澜薄。

Q:一點疑問肤京,如果是自簽證書棋枕,在獲取本地證書公鑰和服務(wù)器證書公鑰的函數(shù)中戒悠,是怎么在沒有設(shè)置錨點證書的情況下绸狐,通過SecTrustEvaluate檢驗證書可信的寒矿?

//取出服務(wù)器返回的證書鏈上所有證書
static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
    CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);//獲取評估證書鏈中的證書數(shù)目符相。
    NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
    //遍歷獲取證書鏈中的每個證書,并添加到trustChain中//獲取的順序蓝牲,從證書鏈的葉節(jié)點到根節(jié)點
    for (CFIndex i = 0; i < certificateCount; i++) {
        SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);//取得證書鏈中對應(yīng)下標(biāo)的證書
        //返回der格式的x.509證書
        [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
    }

    return [NSArray arrayWithArray:trustChain];
}

2.AFServerTrustIsValid

//驗證serverTrust是否有效
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
    BOOL isValid = NO;
    SecTrustResultType result;
    __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);//評估證書是否可信例衍,確認(rèn)它是值得信任的.
    /*SecTrustResultType結(jié)果有兩個維度佛玄。 1.serverTrust評估是否成功,2.是否由用戶決定評估成功愧哟。
     如果是用戶決定的,成功是 kSecTrustResultProceed 失敗是kSecTrustResultDeny。
     非用戶定義的绩脆, 成功是kSecTrustResultUnspecified 失敗是kSecTrustResultRecoverableTrustFailure
     用戶決策通過使用SecTrustCopyExceptions()和SecTrustSetExceptions()*/
    isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);

 _out:
    return isValid;
}
/*
#ifndef __Require_noErr_Quiet
    #define __Require_noErr_Quiet(errorCode, exceptionLabel)                      \
      do                                                                          \
      {                                                                           \
          if ( __builtin_expect(0 != (errorCode), 0) )                            \
          {                                                                       \
              goto exceptionLabel;                                                \
          }                                                                       \
      } while ( 0 )
#endif
*/

這個函數(shù)核心是用SecTrustEvaluate函數(shù)來驗證serverTrust是否有效,返回一個SecTrustResultType類型的result玉锌。

SecTrustResultType的結(jié)果有兩個維度主守。 1.serverTrust評估是否成功参淫,2.是否由用戶決定評估成功涎才。
如果是用戶決定的(比如系統(tǒng)彈窗出來讓用戶決定是否信任證書)耍铜,成功是kSecTrustResultProceed失敗是kSecTrustResultDeny棕兼。
非用戶定義的程储, 成功是kSecTrustResultUnspecified失敗是kSecTrustResultRecoverableTrustFailure

關(guān)于__Require_noErr_Quiet這個宏章鲤,是用來判斷errorCode是否為0的败徊,如果不為0就跳到exceptionLabel標(biāo)簽處執(zhí)行代碼皱蹦。所以這里的意思就是沪哺,如果SecTrustEvaluate評估出錯辜妓,就跳到_out標(biāo)簽?zāi)菆?zhí)行代碼令isValid=0籍滴。

以下用到的原生c函數(shù):

//1.創(chuàng)建用于評估SSL證書鏈的策略對象晚岭。第一個參數(shù):true將為SSL服務(wù)器證書創(chuàng)建一個策略坦报。第二個參數(shù):如果這個參數(shù)存在燎竖,證書鏈上的葉子節(jié)點表示的那個domain要和傳入的domain相匹配
SecPolicyCreateSSL(<#Boolean server#>, <#CFStringRef  _Nullable hostname#>)
//2.默認(rèn)的BasicX509驗證策略,不驗證域名构回。
SecPolicyCreateBasicX509();
//3.為serverTrust設(shè)置驗證策略
SecTrustSetPolicies(<#SecTrustRef  _Nonnull trust#>, <#CFTypeRef  _Nonnull policies#>)
//4.驗證serverTrust,并且把驗證結(jié)果返回給第二參數(shù) result
//函數(shù)內(nèi)部遞歸地從葉節(jié)點證書到根證書驗證纤掸。使用系統(tǒng)默認(rèn)的驗證方式驗證Trust Object,根據(jù)上述證書鏈的驗證可知掏愁,系統(tǒng)會根據(jù)Trust Object的驗證策略果港,一級一級往上辛掠,驗證證書鏈上每一級證書有效性萝衩。
SecTrustEvaluate(<#SecTrustRef  _Nonnull trust#>, <#SecTrustResultType * _Nullable result#>)
//5.根據(jù)證書data,去創(chuàng)建SecCertificateRef類型的數(shù)據(jù)猩谊。
SecCertificateCreateWithData(<#CFAllocatorRef  _Nullable allocator#>, <#CFDataRef  _Nonnull data#>)
//6.給serverTrust設(shè)置錨點證書牌捷,即如果以后再次去驗證serverTrust宜鸯,會從錨點證書去找是否匹配淋袖。
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
//7.拿到證書鏈中的證書個數(shù)
SecTrustGetCertificateCount(serverTrust);
//8.去取得證書鏈中對應(yīng)下標(biāo)的證書。
SecTrustGetCertificateAtIndex(serverTrust, i)
//8.根據(jù)證書獲取公鑰剥懒。
SecTrustCopyPublicKey(trust)

5.4在AF中的調(diào)用

- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    /*挑戰(zhàn)處理類型
            NSURLSessionAuthChallengeUseCredential              使用指定證書
            NSURLSessionAuthChallengePerformDefaultHandling     默認(rèn)方式處理
            NSURLSessionAuthChallengeCancelAuthenticationChallenge  取消挑戰(zhàn)The entire request will be canceled; the credential parameter is ignored
            NSURLSessionAuthChallengeRejectProtectionSpace拒接認(rèn)證請求。
     */
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;

    //sessionDidReceiveAuthenticationChallenge是自定義方法保檐,用來處理如何應(yīng)對服務(wù)器端的認(rèn)證挑戰(zhàn)
    if (self.sessionDidReceiveAuthenticationChallenge) {
        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
    } else {
        // 也就是說服務(wù)器端需要客戶端返回一個根據(jù)認(rèn)證挑戰(zhàn)的保護(hù)空間提供的信任(即challenge.protectionSpace.serverTrust)產(chǎn)生的挑戰(zhàn)證書夜只。
        //要求對保護(hù)空間執(zhí)行服務(wù)器證書認(rèn)證
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            // 基于客戶端的安全策略來決定是否信任該服務(wù)器,不信任的話谈为,也就沒必要響應(yīng)挑戰(zhàn)
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                // 創(chuàng)建挑戰(zhàn)證書
                //創(chuàng)建并返回一個NSURLCredential對象谦铃,以使用給定的可接受的信任進(jìn)行服務(wù)器信任身份驗證驹闰。
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                // 確定挑戰(zhàn)的方式
                if (credential) {
                    //證書挑戰(zhàn)  設(shè)計policy,none嘹朗,則跑到這里
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                //取消挑戰(zhàn)
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            //默認(rèn)挑戰(zhàn)方式
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }
    //完成挑戰(zhàn)
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

這個方法大概做了什么:
1)首先指定了處理認(rèn)證挑戰(zhàn)的默認(rèn)方式默穴。
2)判斷有沒有自定義Block:sessionDidReceiveAuthenticationChallenge蓄诽,有的話仑氛,使用我們自定義Block,自定義處理應(yīng)對服務(wù)器端的認(rèn)證挑戰(zhàn)锯岖。
3)如果沒有自定義Block出吹,我們判斷如果服務(wù)端的認(rèn)證方法要求是NSURLAuthenticationMethodServerTrust,則只需要驗證服務(wù)端證書是否安全(即https的單向認(rèn)證捶牢,這是AF默認(rèn)處理的認(rèn)證方式叫确,其他的認(rèn)證方式竹勉,只能由我們自定義Block的實現(xiàn))
3.1)接著我們就執(zhí)行了AFSecurityPolicy相關(guān)的一個方法,做了一個AF內(nèi)部的一個對服務(wù)器的認(rèn)證:
[self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host])
AF默認(rèn)的處理是娄琉,如果這行返回NO次乓、說明AF內(nèi)部認(rèn)證失敗,則取消https認(rèn)證孽水,即取消請求票腰。返回YES則進(jìn)入if塊,用服務(wù)器返回的一個serverTrust去生成了一個認(rèn)證證書女气。然后如果有證書杏慰,則用證書認(rèn)證方式缘滥,否則還是用默認(rèn)的驗證方式擎颖。
最后調(diào)用completionHandler傳遞認(rèn)證方式和要認(rèn)證的證書允跑,去做系統(tǒng)根證書驗證潮针。

總結(jié):這里securityPolicy存在的作用就是,使得在系統(tǒng)底層自己去驗證之前,AF可以先去驗證服務(wù)端的證書。如果通不過,則直接越過系統(tǒng)的驗證,取消https的網(wǎng)絡(luò)請求夕春。否則速侈,繼續(xù)去走系統(tǒng)根證書的驗證捅僵。
系統(tǒng)驗證的流程:
系統(tǒng)的驗證趴樱,首先是去系統(tǒng)的根證書找疏虫,看是否有能匹配服務(wù)端的證書斯议,如果匹配看靠,則驗證成功,返回https的安全數(shù)據(jù)。
如果不匹配則去判斷ATS是否關(guān)閉旁舰,如果關(guān)閉,則返回https不安全連接的數(shù)據(jù)紧阔。如果開啟ATS憾儒,則拒絕這個請求边琉,請求失敗。
AF的驗證方式不是必須的类茂,但是對有特殊驗證需求的用戶確是必要的。

系統(tǒng)api上的一些用法記錄:

  • NSURLAuthenticationChallenge
@property (readonly, copy) NSURLProtectionSpace *protectionSpace;
NSURLProtectionSpace對象,受保護(hù)空間冒滩,代表了服務(wù)器上的一塊需要授權(quán)信息的區(qū)域。包括了服務(wù)器地址、端口等信息限煞。

 @property (nullable, readonly, copy) NSURLCredential *proposedCredential;
這個認(rèn)證挑戰(zhàn) 建議使用的證書

 @property (readonly) NSInteger previousFailureCount;
認(rèn)證失敗的次數(shù)

 @property (nullable, readonly, copy) NSURLResponse *failureResponse;
最后一次認(rèn)證失敗的響應(yīng)信息

 @property (nullable, readonly, copy) NSError *error;
認(rèn)真失敗的錯誤信息

 @property (nullable, readonly, retain) id<NSURLAuthenticationChallengeSender> sender;
代理對象钦听,challenge的發(fā)送者扮饶, NSURLSession仪召、connection對象之類的

NSURLAuthenticationChallenge類型的參數(shù)簡單理解來說店读,就是服務(wù)端發(fā)起的認(rèn)證挑戰(zhàn)彼棍,客戶端要根據(jù)認(rèn)證挑戰(zhàn)的類型提供響應(yīng)的挑戰(zhàn)憑證(NSURLCredential)

由于- URLSession:didReceiveChallenge:completionHandler:回調(diào)時不止HTTPS服務(wù)器身份鑒別咸产,因此首先判斷一下身份鑒別的類型。通過challenge.protectionSpace.authenticationMethod可以獲取验庙。NSURLAuthenticationMethodServerTrust指對protectionSpace執(zhí)行服務(wù)器證書驗證违寿。

響應(yīng)挑戰(zhàn)
通過sender代理實例阱当,讓客戶端來選擇怎樣的挑戰(zhàn)響應(yīng)方式。

// 用憑證響應(yīng)挑戰(zhàn)。如果是雙向驗證眠冈,不僅客戶端要驗證服務(wù)器身份飞苇,服務(wù)器也需要客戶端提供證書,因此需要提供憑證
 - (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
//不提供憑證繼續(xù)請求
 - (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
//取消憑證驗證
 - (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
//使用默認(rèn)方式處理認(rèn)證挑戰(zhàn)
 - (void)performDefaultHandlingForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
//拒絕當(dāng)前提供的受保護(hù)空間并且嘗試不提供憑證繼續(xù)請求
 - (void)rejectProtectionSpaceAndContinueWithChallenge:(NSURLAuthenticationChallenge *)challenge;
  • NSURLCredential
    表示身份驗證證書(憑證)蜗顽。URL Lodaing支持3種類型證書:password-based user credentials, certificate-based user credentials, 和certificate-based server credentials(需要驗證服務(wù)器身份時使用)玄柠。NSURLCredential可以表示由用戶名/密碼組合、客戶端證書及服務(wù)器信任創(chuàng)建的認(rèn)證信息诫舅,適合大部分的認(rèn)證請求羽利。
    對于NSURLCredential也存在三種持久化機制:

    • NSURLCredentialPersistenceNone :要求 URL 載入系統(tǒng) “在用完相應(yīng)的認(rèn)證信息后立刻丟棄”。
  • NSURLCredentialPersistenceForSession :要求 URL 載入系統(tǒng) “在應(yīng)用終止時刊懈,丟棄相應(yīng)的 credential ”这弧。

  • NSURLCredentialPersistencePermanent :要求 URL 載入系統(tǒng) “將相應(yīng)的認(rèn)證信息存入鑰匙串(keychain),以便其他應(yīng)用也能使用虚汛。

  • 如何處理挑戰(zhàn)匾浪。 NSURLSessionAuthChallengeDisposition類型的數(shù)據(jù),是一個常數(shù):

NSURLSessionAuthChallengeUseCredential              使用指定證書
NSURLSessionAuthChallengePerformDefaultHandling     默認(rèn)方式處理
NSURLSessionAuthChallengeCancelAuthenticationChallenge  取消挑戰(zhàn)The entire request will be canceled; the credential parameter is ignored
NSURLSessionAuthChallengeRejectProtectionSpace拒接認(rèn)證請求卷哩。

詳細(xì)源碼注釋請戳:https://github.com/huixinHu/AFNetworking-

參考文章:
圖解SSL/TLS協(xié)議
iOS安全系列之二:HTTPS進(jìn)階
iOS 中 HTTPS 證書驗證淺析
iOS 中對 HTTPS 證書鏈的驗證
AFNetworking之于https認(rèn)證

深入理解HTTPS及在iOS系統(tǒng)中適配HTTPS類型網(wǎng)絡(luò)請求(上)
深入理解HTTPS及在iOS系統(tǒng)中適配HTTPS類型網(wǎng)絡(luò)請求(下)
iOS HPPTS證書驗證

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蛋辈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冷溶,老刑警劉巖渐白,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異逞频,居然都是意外死亡纯衍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門苗胀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來襟诸,“玉大人,你說我怎么就攤上這事基协「枨祝” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵澜驮,是天一觀的道長应结。 經(jīng)常有香客問我,道長泉唁,這世上最難降的妖魔是什么鹅龄? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮亭畜,結(jié)果婚禮上扮休,老公的妹妹穿的比我還像新娘。我一直安慰自己拴鸵,他們只是感情好玷坠,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著劲藐,像睡著了一般八堡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上聘芜,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天兄渺,我揣著相機與錄音,去河邊找鬼汰现。 笑死挂谍,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瞎饲。 我是一名探鬼主播口叙,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嗅战!你這毒婦竟也來了妄田?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疟呐,沒想到半個月后脚曾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡萨醒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年斟珊,在試婚紗的時候發(fā)現(xiàn)自己被綠了苇倡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片富纸。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖旨椒,靈堂內(nèi)的尸體忽然破棺而出晓褪,到底是詐尸還是另有隱情,我是刑警寧澤综慎,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布涣仿,位于F島的核電站,受9級特大地震影響示惊,放射性物質(zhì)發(fā)生泄漏好港。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一米罚、第九天 我趴在偏房一處隱蔽的房頂上張望钧汹。 院中可真熱鬧,春花似錦录择、人聲如沸拔莱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽塘秦。三九已至,卻和暖如春动看,著一層夾襖步出監(jiān)牢的瞬間尊剔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工菱皆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赋兵,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓搔预,卻偏偏與公主長得像霹期,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拯田,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

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