本想在這篇文章中單獨寫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四次握手
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)使用證書的過程如下圖所示:
公鑰基礎(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和重要屬性
- 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ù)端證書拷貝
- 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類型
的serverTrust
和NSString
類型的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證書驗證