AFNetworking源碼探究(十四) —— AFSecurityPolicy與安全認(rèn)證 (二)

版本記錄

版本號(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)返回類型為SecCertificateRefSecCertificateCreateWithData函數(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)求失敗。

參考文章

1. AFNetworking之于https認(rèn)證

后記

本篇詳述了AFSecurityPolicy驗(yàn)證服務(wù)端的流程,借鑒了很多大神的博客和文章。這里面已經(jīng)引用列出來(lái)了,表示感謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容