四、Security部分
顧名思義這塊是AFNetworking中的安全框架,在iOS9之后網(wǎng)絡(luò)請(qǐng)求中加入SSL使用HTTPS幾乎已經(jīng)成為蘋果的強(qiáng)制要求了。Security這部分主要功能就是用來(lái)驗(yàn)證HTTPS的證書是否有效。這部分主要是通過(guò)AFSecurityPolicy這個(gè)類來(lái)實(shí)現(xiàn)的。
下面還是通過(guò)一個(gè)具體的調(diào)用來(lái)分析,就從AFNetworking的默認(rèn)調(diào)用開始,之前在分析NSURLSession部分時(shí)提到過(guò),在AFURLSessionManager的初始化方法中設(shè)置:

首先分析一下AFSecurityPolicy的這個(gè)初始化方法:


可看出在這一系列的初始化方法中主要設(shè)置了這兩個(gè)屬性,securityPolicy.SSLPinningMode = AFSSLPinningModeNone和self.validatesDomainName = YES。

表示是否驗(yàn)證域名,默認(rèn)是YES。


AFSSLPinningModeNone表示不使用固定證書(本地)驗(yàn)證服務(wù)器。直接從客戶端系統(tǒng)中的受信任頒發(fā)機(jī)構(gòu) CA 列表中去驗(yàn)證。
AFSSLPinningModePublicKey代表會(huì)對(duì)服務(wù)器返回的證書中的PublicKey進(jìn)行驗(yàn)證,通過(guò)則通過(guò),否則不通過(guò)。
AFSSLPinningModeCertificate代表會(huì)對(duì)服務(wù)器返回的證書同本地證書全部進(jìn)行校驗(yàn),通過(guò)則通過(guò),否則不通過(guò)。
可看出AFNetworking默認(rèn)使用的是AFSSLPinningModeNone模式。
繼續(xù)查看發(fā)現(xiàn)AFURLSessionManager會(huì)在這兩個(gè)地方使用這個(gè)屬性:


這兩個(gè)方法分別是實(shí)現(xiàn)了NSURLSessionDelegate和NSURLSessionTaskDelegate的協(xié)議方法。然后通過(guò)這方法里實(shí)現(xiàn)https的認(rèn)證。服務(wù)端發(fā)起的一個(gè)驗(yàn)證挑戰(zhàn),客戶端需要根據(jù)挑戰(zhàn)的類型提供相應(yīng)的挑戰(zhàn)憑證。當(dāng)然,挑戰(zhàn)憑證不一定都是進(jìn)行HTTPS證書的信任,也可能是需要客戶端提供用戶密碼或者提供雙向驗(yàn)證時(shí)的客戶端證書。當(dāng)這個(gè)挑戰(zhàn)憑證被驗(yàn)證通過(guò)時(shí),請(qǐng)求便可以繼續(xù)順利進(jìn)行。下面具體分析認(rèn)證過(guò)程:

NSURLAuthenticationChallenge*類型的challenge 表示一個(gè)認(rèn)證的挑戰(zhàn),提供了關(guān)于這次認(rèn)證的全部信息。接著看:

challenge的protectionSpace屬性保存了需要認(rèn)證的保護(hù)空間,NSURLProtectionSpace *類型的protectionSpace主機(jī)地址,端口和認(rèn)證方法等重要信息。由上邊代碼可知如果protectionSpace.authenticationMethod是NSURLAuthenticationMethodServerTrust,就對(duì)保護(hù)空間中的?serverTrust?以及域名host進(jìn)行認(rèn)證,并根據(jù)認(rèn)證的結(jié)果,會(huì)在?completionHandler?中傳入不同的disposition?和credential參數(shù)。
繼續(xù)深入分析AFSecurityPolicy的認(rèn)證方法:



第一個(gè)條件分支

如果有服務(wù)器域名、設(shè)置了允許信任無(wú)效或者過(guò)期證書(自簽名證書)、需要驗(yàn)證域名、沒(méi)有提供證書或者不驗(yàn)證證書,返回NO。

這一步為設(shè)置驗(yàn)證策略,如果要驗(yàn)證域名,就以域名為參數(shù)創(chuàng)建一個(gè)策略,否則創(chuàng)建默認(rèn)的basicX509策略。

這一步是驗(yàn)證證書的有效性,如果是AFSSLPinningModeNone,不做本地證書驗(yàn)證,從客戶端系統(tǒng)中的受信任頒發(fā)機(jī)構(gòu) CA 列表中去驗(yàn)證服務(wù)端返回的證書。但是如果不做本地證書驗(yàn)證,從客戶端系統(tǒng)中的受信任頒發(fā)機(jī)構(gòu) CA 列表中去驗(yàn)證服務(wù)端返回的證書通不過(guò)的話,而且allowInvalidCertificates不允許自簽,返回NO。
調(diào)用下面這個(gè)方法進(jìn)行驗(yàn)證有效性:

可看到這個(gè)方法里邊都是對(duì)系統(tǒng)庫(kù)<Security/Security.h>中方法的調(diào)用。
接下來(lái)是通過(guò)不同的self.SSLPinningMode進(jìn)行對(duì)服務(wù)器信任的驗(yàn)證。

如果是AFSSLPinningModeNone直接返回NO。
如果是AFSSLPinningModeCertificate:

首先把證書data用系統(tǒng)方法SecCertificateCreateWithData轉(zhuǎn)成 SecCertificateRef 類型的數(shù)據(jù),再調(diào)用系統(tǒng)方法SecTrustSetAnchorCertificates將pinnedCertificates設(shè)置成需要參與驗(yàn)證的Anchor Certificate(錨點(diǎn)證書)。

參與校驗(yàn)錨點(diǎn)證書之后,假如驗(yàn)證的數(shù)字證書是這個(gè)錨點(diǎn)證書的子節(jié)點(diǎn),即驗(yàn)證的數(shù)字證書是由錨點(diǎn)證書對(duì)應(yīng)CA或子CA簽發(fā)的,或是該證書本身,則信任該證書),具體就是調(diào)用SecTrustEvaluate來(lái)驗(yàn)證(如上)。
接下來(lái)這塊:

從我們需要被驗(yàn)證的服務(wù)端證書,去拿證書鏈,如果我們的證書中,有一個(gè)和它證書鏈中的證書匹配的,就返回YES。否則沒(méi)有匹配。(注意看:在匹配時(shí)調(diào)用了[serverCertificates reverseObjectEnumerator]這個(gè)方法得到一個(gè)前邊獲取的數(shù)組反轉(zhuǎn)后的數(shù)組,反轉(zhuǎn)前是從葉子節(jié)點(diǎn)到根節(jié)點(diǎn),反轉(zhuǎn)后是從根節(jié)點(diǎn)到葉子節(jié)點(diǎn)。)
這個(gè)函數(shù)是通過(guò)調(diào)用系統(tǒng)的相關(guān)方法來(lái)獲取證書鏈。

如果是AFSSLPinningModePublicKey:

先調(diào)用AFPublicKeyTrustChainForServerTrust方法從serverTrust中取出服務(wù)器端傳過(guò)來(lái)的所有可用的證書,并依次得到相應(yīng)的公鑰。再通過(guò)雙重遍歷并調(diào)用方法AFSecKeyIsEqualToKey對(duì)比本地的證書公鑰和服務(wù)器傳過(guò)來(lái)的證書公鑰,如果相同的對(duì)數(shù)大于0,則說(shuō)明驗(yàn)證通過(guò)。
就此整個(gè)使用過(guò)程分析結(jié)束。和上篇的AFNetworkReachabilityManager類似,AFSecurityPolicy是對(duì)系統(tǒng)C語(yǔ)言安全庫(kù)<Security>的一個(gè)OC封裝,提供了簡(jiǎn)便易用的OC接口。