版本記錄
| 版本號(hào) | 時(shí)間 |
|---|---|
| V1.0 | 2018.03.02 |
前言
我們做APP發(fā)起網(wǎng)絡(luò)請(qǐng)求,都離不開(kāi)一個(gè)非常有用的框架AFNetworking,可以說(shuō)這個(gè)框架的知名度已經(jīng)超過(guò)了蘋果的底層網(wǎng)絡(luò)請(qǐng)求部分,很多人可能不知道蘋果底層是如何發(fā)起網(wǎng)絡(luò)請(qǐng)求的,但是一定知道
AFNetworking,接下來(lái)幾篇我們就一起詳細(xì)的解析一下這個(gè)框架。感興趣的可以看上面寫的幾篇。
1. AFNetworking源碼探究(一) —— 基本介紹
2. AFNetworking源碼探究(二) —— GET請(qǐng)求實(shí)現(xiàn)之NSURLSessionDataTask實(shí)例化(一)
3. AFNetworking源碼探究(三) —— GET請(qǐng)求實(shí)現(xiàn)之任務(wù)進(jìn)度設(shè)置和通知監(jiān)聽(tīng)(一)
4. AFNetworking源碼探究(四) —— GET請(qǐng)求實(shí)現(xiàn)之代理轉(zhuǎn)發(fā)思想(一)
5. AFNetworking源碼探究(五) —— AFURLSessionManager中NSURLSessionDelegate詳細(xì)解析(一)
6. AFNetworking源碼探究(六) —— AFURLSessionManager中NSURLSessionTaskDelegate詳細(xì)解析(一)
7. AFNetworking源碼探究(七) —— AFURLSessionManager中NSURLSessionDataDelegate詳細(xì)解析(一)
8. AFNetworking源碼探究(八) —— AFURLSessionManager中NSURLSessionDownloadDelegate詳細(xì)解析(一)
9. AFNetworking源碼探究(九) —— AFURLSessionManagerTaskDelegate中三個(gè)轉(zhuǎn)發(fā)代理方法詳細(xì)解析(一)
10. AFNetworking源碼探究(十) —— 數(shù)據(jù)解析之?dāng)?shù)據(jù)解析架構(gòu)的分析(一)
11. AFNetworking源碼探究(十一) —— 數(shù)據(jù)解析之子類中協(xié)議方法的實(shí)現(xiàn)(二)
12. AFNetworking源碼探究(十二) —— 數(shù)據(jù)解析之子類中協(xié)議方法的實(shí)現(xiàn)(三)
13. AFNetworking源碼探究(十三) —— AFSecurityPolicy與安全認(rèn)證 (一)
回顧
上一篇主要講述了HTTPS認(rèn)證原理以及AFSecurityPolicy的實(shí)例化。這一篇就具體的看一下驗(yàn)證流程。
驗(yàn)證服務(wù)端
還記得上一篇那個(gè)驗(yàn)證服務(wù)端的那個(gè)方法嗎?具體如下表示。
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
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;
}
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
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)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
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;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
}
下面我們就看一下這個(gè)驗(yàn)證的過(guò)程。
(a) 第一個(gè)if判斷
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;
}
首先看一下判斷條件,如果域名存在,且允許自建證書,且需要驗(yàn)證域名,且SSLPinningMode模式為AFSSLPinningModeNone或者添加到項(xiàng)目中的證書數(shù)量為0個(gè)。
只要滿足上面的if條件,就返回NO,表示不信任。
(b) 安全策略
主要對(duì)應(yīng)下面這段代碼
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
首先就是實(shí)例化一個(gè)可變數(shù)組,用于后面函數(shù)SecTrustSetPolicies中做參數(shù),接著就是根據(jù)條件self.validatesDomainName,為數(shù)組添加不同的元素。
- 如果
self.validatesDomainName == YES,需要驗(yàn)證域名,那么調(diào)用下面函數(shù),這個(gè)函數(shù)是Security框架中的,是蘋果原生的,返回值類型為SecPolicyRef,將該返回值加入到策略數(shù)組policies中。
如果需要驗(yàn)證domain,那么就使用SecPolicyCreateSSL函數(shù)創(chuàng)建驗(yàn)證策略,其中第一個(gè)參數(shù)為true表示驗(yàn)證整個(gè)SSL證書鏈,第二個(gè)參數(shù)傳入domain,用于判斷整個(gè)證書鏈上葉子節(jié)點(diǎn)表示的那個(gè)domain是否和此處傳入domain一致。
我們先看第一個(gè)函數(shù)
/*!
@function SecPolicyCreateSSL
@abstract Returns a policy object for evaluating SSL certificate chains.
// 返回用于評(píng)估SSL證書鏈的策略對(duì)象。
@param server Passing true for this parameter creates a policy for SSL
server certificates.
// 服務(wù)器為此參數(shù)傳遞true將為SSL服務(wù)器證書創(chuàng)建策略
@param hostname (Optional) If present, the policy will require the specified
hostname to match the hostname in the leaf certificate.
// 如果存在,策略將要求指定的主機(jī)名與葉證書中的主機(jī)名匹配。
@result A policy object. The caller is responsible for calling CFRelease
on this when it is no longer needed.
*/
SecPolicyRef SecPolicyCreateSSL(Boolean server, CFStringRef __nullable hostname)
// 策略對(duì)象。 調(diào)用者負(fù)責(zé)在不再需要時(shí)調(diào)用CFRelease
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);
- 如果
self.validatesDomainName == NO,不需要驗(yàn)證域名,那么調(diào)用下面函數(shù),這個(gè)函數(shù)是Security框架中的,是蘋果原生的,返回值類型為SecPolicyRef,將該返回值加入到策略數(shù)組policies中。如果不需要驗(yàn)證domain,就使用默認(rèn)的BasicX509驗(yàn)證策略
看一下這個(gè)函數(shù)
/*!
@function SecPolicyCreateBasicX509
@abstract Returns a policy object for the default X.509 policy.
// 返回默認(rèn)X.509策略的策略對(duì)象
@result A policy object. The caller is responsible for calling CFRelease
on this when it is no longer needed.
*/
// 策略對(duì)象。 調(diào)用者負(fù)責(zé)調(diào)用CFRelease在不再需要它時(shí)進(jìn)行調(diào)用釋放
SecPolicyRef SecPolicyCreateBasicX509(void)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);
最后調(diào)用函數(shù)SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);函數(shù)設(shè)置策略,這里serverTrust:X.509服務(wù)器的證書信任;policies表示為serverTrust設(shè)置驗(yàn)證策略,即告訴客戶端如何驗(yàn)證serverTrust。具體這個(gè)函數(shù)的接口如下所示:
/*!
@function SecTrustSetPolicies
@abstract Set the policies for which trust should be verified.
@param trust A trust reference.
@param policies An array of one or more policies. You may pass a
SecPolicyRef to represent a single policy.
@result A result code. See "Security Error Codes" (SecBase.h).
@discussion This function will invalidate the existing trust result,
requiring a fresh evaluation for the newly-set policies.
*/
OSStatus SecTrustSetPolicies(SecTrustRef trust, CFTypeRef policies)
__OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_6_0);
(c) 驗(yàn)證模式的判斷
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
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)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
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;
}
}
}
return trustedPublicKeyCount > 0;
}
}
這里,前面已經(jīng)有驗(yàn)證策略了,可以去驗(yàn)證了,首先判斷self.SSLPinningMode == AFSSLPinningModeNone,然后看這一句self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust),如果支持自簽名,直接返回YES,不允許才去判斷第二個(gè)條件,判斷serverTrust是否有效,下面的是驗(yàn)證的函數(shù)。
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
BOOL isValid = NO;
SecTrustResultType result;
__Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
_out:
return isValid;
}
如果驗(yàn)證無(wú)效AFServerTrustIsValid,而且allowInvalidCertificates不允許自簽,返回NO。
else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
接著就是switch條件句判斷SSLPinningMode這個(gè)驗(yàn)證模式。
- 如果是
AFSSLPinningModeNone
case AFSSLPinningModeNone:
default:
return NO;
這種就是默認(rèn)返回的NO。
- 如果是
AFSSLPinningModeCertificate
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
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)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
這個(gè)case表示驗(yàn)證證書類型。
首先實(shí)例化一個(gè)可變數(shù)組
NSMutableArray *pinnedCertificates = [NSMutableArray array];
下面看一個(gè)集合屬性
/**
The certificates used to evaluate server trust according to the SSL pinning mode.
// 用于根據(jù)SSL固定模式評(píng)估服務(wù)器信任的證書
By default, this property is set to any (`.cer`) certificates included in the target compiling AFNetworking. Note that if you are using AFNetworking as embedded framework, no certificates will be pinned by default. Use `certificatesInBundle` to load certificates from your target, and then create a new policy by calling `policyWithPinningMode:withPinnedCertificates`.
Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches.
*/
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
默認(rèn)情況下,該屬性設(shè)置為包含在目標(biāo)編譯AFNetworking中的任何(.cer)證書。 請(qǐng)注意,如果您使用AFNetworking作為嵌入式框架,則默認(rèn)情況下不會(huì)鎖定任何證書。 使用certificatesInBundle從你的目標(biāo)加載證書,然后通過(guò)調(diào)用policyWithPinningMode:withPinnedCertificates來(lái)創(chuàng)建一個(gè)新的策略。
請(qǐng)注意,如果啟用了鎖定,如果任何固定證書匹配,evaluateServerTrust:forDomain:將返回true。
接著就是對(duì)該集合對(duì)象進(jìn)行遍歷
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
把證書數(shù)據(jù),用系統(tǒng)返回類型為SecCertificateRef的SecCertificateCreateWithData函數(shù)對(duì)原先的certificateData做一些處理,保證返回的證書都是DER編碼的X.509證書。下面看一下這個(gè)函數(shù)。
/*!
@function SecCertificateCreateWithData
@abstract Create a certificate given it's DER representation as a CFData.
@param allocator CFAllocator to allocate the certificate with.
@param data DER encoded X.509 certificate.
@result Return NULL if the passed-in data is not a valid DER-encoded
X.509 certificate, return a SecCertificateRef otherwise.
*/
__nullable
SecCertificateRef SecCertificateCreateWithData(CFAllocatorRef __nullable allocator, CFDataRef data)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);
然后,將pinnedCertificates設(shè)置成需要參與驗(yàn)證的Anchor Certificate(錨點(diǎn)證書,通過(guò)SecTrustSetAnchorCertificates設(shè)置了參與校驗(yàn)錨點(diǎn)證書之后,假如驗(yàn)證的數(shù)字證書是這個(gè)錨點(diǎn)證書的子節(jié)點(diǎn),即驗(yàn)證的數(shù)字證書是由錨點(diǎn)證書對(duì)應(yīng)CA或子CA簽發(fā)的,或是該證書本身,則信任該證書)。先看一下這個(gè)函數(shù)。
/*!
@function SecTrustSetAnchorCertificates
@abstract Sets the anchor certificates for a given trust.
@param trust A reference to a trust object.
@param anchorCertificates An array of anchor certificates.
@result A result code. See "Security Error Codes" (SecBase.h).
@discussion Calling this function without also calling
SecTrustSetAnchorCertificatesOnly() will disable trusting any
anchors other than the ones in anchorCertificates.
*/
OSStatus SecTrustSetAnchorCertificates(SecTrustRef trust,
CFArrayRef anchorCertificates)
__OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_2_0);
然后,接著進(jìn)行判斷
if (!AFServerTrustIsValid(serverTrust)) {
return NO;
}
自簽在之前是驗(yàn)證通過(guò)不了的,在這一步,把我們自己設(shè)置的證書加進(jìn)去之后,就能驗(yàn)證成功了。再去調(diào)用之前的serverTrust去驗(yàn)證該證書是否有效,有可能經(jīng)過(guò)這個(gè)方法過(guò)濾后,serverTrust里面的pinnedCertificates被篩選到只有信任的那一個(gè)證書。
最后,還是獲取一個(gè)數(shù)組并遍歷,這個(gè)方法和我們之前的錨點(diǎn)證書沒(méi)關(guān)系了,是去從我們需要被驗(yàn)證的服務(wù)端證書,去拿證書鏈。這個(gè)數(shù)組是服務(wù)器端的證書鏈,注意此處返回的證書鏈順序是從葉節(jié)點(diǎn)到根節(jié)點(diǎn)。
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
如果我們的證書中,有一個(gè)和它證書鏈中的證書匹配的,就返回YES,否則就返回NO。
- 如果是
AFSSLPinningModePublicKey
公鑰驗(yàn)證AFSSLPinningModePublicKey模式同樣是用證書綁定(SSL Pinning)方式驗(yàn)證,客戶端要有服務(wù)端的證書拷貝,只是驗(yàn)證時(shí)只驗(yàn)證證書里的公鑰,不驗(yàn)證證書的有效期等信息。只要公鑰是正確的,就能保證通信不會(huì)被竊聽(tīng),因?yàn)橹虚g人沒(méi)有私鑰,無(wú)法解開(kāi)通過(guò)公鑰加密的數(shù)據(jù)。
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
// 遍歷服務(wù)端公鑰
for (id trustChainPublicKey in publicKeys) {
// 遍歷本地公鑰
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
這里利用AFN函數(shù)的自定義函數(shù)獲取公鑰的數(shù)組集合。
static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
SecPolicyRef policy = SecPolicyCreateBasicX509();
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
SecCertificateRef someCertificates[] = {certificate};
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);
SecTrustRef trust;
__Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
SecTrustResultType result;
__Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
[trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
_out:
if (trust) {
CFRelease(trust);
}
if (certificates) {
CFRelease(certificates);
}
continue;
}
CFRelease(policy);
return [NSArray arrayWithArray:trustChain];
}
從serverTrust中取出服務(wù)器端傳過(guò)來(lái)的所有可用的證書,并依次得到相應(yīng)的公鑰。然后遍歷服務(wù)端的公鑰,內(nèi)部嵌套一個(gè)遍歷是遍歷本地公鑰,如果相同那么trustedPublicKeyCount + 1,如果trustedPublicKeyCount > 0,就是YES,否則就是NO。下面是比較函數(shù)。
static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
return [(__bridge id)key1 isEqual:(__bridge id)key2];
#else
return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)];
#endif
}
總結(jié)
驗(yàn)證流程
- 根據(jù)模式,如果是
AFSSLPinningModeNone,則肯定是返回YES,不論是自簽還是公信機(jī)構(gòu)的證書。 - 如果是
AFSSLPinningModeCertificate,則從serverTrust中去獲取證書鏈,然后和我們一開(kāi)始初始化設(shè)置的證書集合self.pinnedCertificates去匹配,如果有一對(duì)能匹配成功的,就返回YES,否則NO。 - 如果是
AFSSLPinningModePublicKey公鑰驗(yàn)證,則和第二步一樣還是從serverTrust,獲取證書鏈每一個(gè)證書的公鑰,放到數(shù)組中。和我們的self.pinnedPublicKeys,去配對(duì),如果有一個(gè)相同的,就返回YES,否則NO。
還需注意
- 如果你用的是付費(fèi)的公信機(jī)構(gòu)頒發(fā)的證書,標(biāo)準(zhǔn)的HTTPS,那么無(wú)論你用的是AF還是NSURLSession,什么都不用做,代理方法也不用實(shí)現(xiàn)。你的網(wǎng)絡(luò)請(qǐng)求就能正常完成。
- 如果你用的是自簽名的證書
- 首先你需要在plist文件中,設(shè)置可以返回不安全的請(qǐng)求(關(guān)閉該域名的ATS)。
- 其次,如果是NSURLSesion,那么需要在代理方法實(shí)現(xiàn)如下
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
__block NSURLCredential *credential = nil;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
// 確定挑戰(zhàn)的方式
if (credential) {
//證書挑戰(zhàn) 則跑到這里
disposition = NSURLSessionAuthChallengeUseCredential;
}
//完成挑戰(zhàn)
if (completionHandler) {
completionHandler(disposition, credential);
}
}
如果是AF,你則需要設(shè)置policy
//允許自簽名證書,必須的
policy.allowInvalidCertificates = YES;
//是否驗(yàn)證域名的CN字段
//不是必須的,但是如果寫YES,則必須導(dǎo)入證書。
policy.validatesDomainName = NO;
AF可以讓你在系統(tǒng)驗(yàn)證證書之前,就去自主驗(yàn)證。然后如果自己驗(yàn)證不正確,直接取消網(wǎng)絡(luò)請(qǐng)求。否則驗(yàn)證通過(guò)則繼續(xù)進(jìn)行系統(tǒng)驗(yàn)證。
系統(tǒng)的驗(yàn)證,首先是去系統(tǒng)的根證書找,看是否有能匹配服務(wù)端的證書,如果匹配,則驗(yàn)證成功,返回https的安全數(shù)據(jù)。如果不匹配則去判斷ATS是否關(guān)閉,如果關(guān)閉,則返回https不安全連接的數(shù)據(jù)。如果開(kāi)啟ATS,則拒絕這個(gè)請(qǐng)求,請(qǐng)求失敗。
參考文章
后記
本篇詳述了
AFSecurityPolicy驗(yàn)證服務(wù)端的流程,借鑒了很多大神的博客和文章。這里面已經(jīng)引用列出來(lái)了,表示感謝。