AFNetworking源碼學(xué)習(xí)之二-HTTPS請求

HTTPS是基于HTTP和SSL的合成,在HTTP的基礎(chǔ)上多了一步證書認(rèn)證的過程,其大致步驟如下:

HTTPS.jpg

1.客戶端向服務(wù)端發(fā)送請求.
2.服務(wù)端返回證書包括公鑰和一些基本信息.服務(wù)端配置證書可向第三方申請證書,此時(shí)不會彈出警告框,也可以自己創(chuàng)建證書,訪問時(shí)會彈出警告框.
3.客戶端驗(yàn)證證書,如果證書不合法,彈出HTTPS警告,合法的話,生成一個(gè)隨機(jī)數(shù),將隨機(jī)數(shù)利用公鑰加密,作為對稱密鑰,傳給服務(wù)器.
4.服務(wù)器利用公鑰解析,得到隨機(jī)數(shù),最為對稱加密的密鑰,將要返回的數(shù)據(jù)進(jìn)行加密傳輸給客戶端.
5.客戶端利用隨機(jī)數(shù)對稱密鑰解密數(shù)據(jù).

這只是單向認(rèn)證,還有雙向認(rèn)證.

下面看看AFN中具體實(shí)現(xiàn):

- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{   // 默認(rèn)處理
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;
    // 自定義驗(yàn)證服務(wù)端挑戰(zhàn)
    if (self.sessionDidReceiveAuthenticationChallenge) {
        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
    } else {
        // 默認(rèn)處理服務(wù)端挑戰(zhàn)(單向認(rèn)證)
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            // 根據(jù)安全策略評估服務(wù)端的信任
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                // 創(chuàng)建信任
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                if (credential) {
                    // 信任挑戰(zhàn)
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    // 默認(rèn)處理挑戰(zhàn)
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                // 取消挑戰(zhàn)
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            // 默認(rèn)處理挑戰(zhàn)
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }
    // 根據(jù)前面的處理和信任,完成挑戰(zhàn)
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

這里主要做了以下幾件事情:
1.如果實(shí)現(xiàn)了自定義的處理挑戰(zhàn),就用自定的處理方式.
2.如果沒有實(shí)現(xiàn)自定義的處理挑戰(zhàn)方式,就用系統(tǒng)的處理方式.

根據(jù)安全策略評估服務(wù)端的信任的代碼如下:

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(NSString *)domain
{   // 如果有域名,且允許自建證書,需要驗(yàn)證域名, AFSSLPinningModeNone或證書個(gè)數(shù)為0時(shí)
    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
        return NO;
    }

    NSMutableArray *policies = [NSMutableArray array];
// 需要驗(yàn)證域名
    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) {
        // 不需要驗(yàn)證
        case AFSSLPinningModeNone:
        default:
            return NO;
        // 需要驗(yàn)證證書
        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;
            }

            NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
            
            for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
                if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                    return YES;
                }
            }
            
            return NO;
        }
        // 需要驗(yàn)證公鑰
        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;
}

對于我們自己做做https請求的話,如果是付費(fèi)證書,我們什么也不用做,如果是自建證書需要在plist文件設(shè)置可以返回不安全的請求,然后AFN已經(jīng)將要做的事情幫我們做了,我們只需寫:

    // 允許自簽名證書
    policy.allowInvalidCertificates = YES;
    // 可以驗(yàn)證域名(需要導(dǎo)入自簽名證書)
    policy.validatesDomainName = YES;

        NSArray *paths = [bundle pathsForResourcesOfType:@"cer"       inDirectory:@"."];
        NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
       for (NSString *path in paths) {
        NSData *certificateData = [NSData      dataWithContentsOfFile:path];
        [certificates addObject:certificateData];
       }
    policy.pinnedCertificates = certificates;

AFN在系統(tǒng)驗(yàn)證之前,幫我們驗(yàn)證了,如果驗(yàn)證通過了走系統(tǒng)的驗(yàn)證,系統(tǒng)驗(yàn)證如果匹配到了證書,就返回安全的鏈接,如果匹配不成功,判斷ATS,ATS關(guān)閉,返回不安全的鏈接,如果ATS開啟,拒絕這個(gè)請求.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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