AFNetworking3.1.0源碼分析(十一)之AFSecurityPolicy驗證服務器信任

通過上篇文章總體的了概述了AFSecurityPolicy的功能。

先了解下數字證書和原理,這篇文章非常詳細的講解了證書以及認證的原理。

下面繼續詳細分析AFSecurityPolicy驗證服務器信任的過程涉及的方法:

1:設置本地證書集合,和獲取本地證書的公鑰並創建公鑰集合

1.1設置本地證書集合

- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
    //保存特定的證書集合
    _pinnedCertificates = pinnedCertificates;

    if (self.pinnedCertificates) {
        NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
        //解析證書中的公鑰
        for (NSData *certificate in self.pinnedCertificates) {
            
            
            id publicKey = AFPublicKeyForCertificate(certificate);
            if (!publicKey) {
                continue;
            }
            NSLog(@"%@",publicKey);
            [mutablePinnedPublicKeys addObject:publicKey];
        }
        self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
    } else {
        self.pinnedPublicKeys = nil;
    }
}
1.2 獲取公鑰函數

static id AFPublicKeyForCertificate(NSData *certificate) {
    id allowedPublicKey = nil;
    SecCertificateRef allowedCertificate;
    SecPolicyRef policy = nil;
    SecTrustRef allowedTrust = nil;
    SecTrustResultType result;
    //DER編碼方式的X.509證書數據來創建SecCertificateRef
    allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
    
//#ifndef __Require_Quiet
//#define __Require_Quiet(assertion, exceptionLabel)                            \
//do                                                                          \
//{                                                                           \
//if ( __builtin_expect(!(assertion), 0) )                                \
//{                                                                       \
//goto exceptionLabel;                                                \
//}                                                                       \
//} while ( 0 )
//#endif
    
//根據以上函數的邏輯,這個地方的含義是如果allowedCertificate 爲null則程序跳轉至_out片段繼續執行
    
    __Require_Quiet(allowedCertificate != NULL, _out);

    //創建X509格式的證書策略(此爲默認的策略)
    policy = SecPolicyCreateBasicX509();
    
//#ifndef __Require_noErr_Quiet
//#define __Require_noErr_Quiet(errorCode, exceptionLabel)                      \
//do                                                                          \
//{                                                                           \
//if ( __builtin_expect(0 != (errorCode), 0) )                            \
//{                                                                       \
//goto exceptionLabel;                                                \
//}                                                                       \
//} while ( 0 )
//#endif
    
//根據以上函數的邏輯,創建SecTrust不成功(返回值 OSStatus 值爲非0)跳轉至_out片段繼續執行
   
    
    __Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
    //SecTrustEvaluate是一個同步處理函數會阻塞線程,作用是評估證書信任,還有個異步函數SecTrustEvaluateAsync,評估結果是非0狀態的話直接跳轉至_out片段繼續執行
    __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);

    //獲公鑰
    allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);

_out:
    if (allowedTrust) {
        CFRelease(allowedTrust);
    }

    if (policy) {
        CFRelease(policy);
    }

    if (allowedCertificate) {
        CFRelease(allowedCertificate);
    }

    return allowedPublicKey;
}

2:iOS系統評估

static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
    
//    //對SecTrustResultType的解釋:
//    typedef CF_ENUM(uint32_t, SecTrustResultType) {
//        kSecTrustResultInvalid  ,//顯示無效的設置或結果。這個結果通常意味着SecTrustEvaluate尚未被調用
//        kSecTrustResultProceed ,//表明用戶可以繼續進行,也就意味着評估是通過的
//        kSecTrustResultConfirm  ,//在用戶需要繼續進行之前確認信息,此值官方已經不贊成使用,在OS X 10.9之後
//        kSecTrustResultDeny  ,//用戶配置拒絕繼續進行,也就意味着評估不通過
//        kSecTrustResultUnspecified  ,//評估成功證書是隱式信任的,但用戶不顯示的指定。
//        kSecTrustResultRecoverableTrustFailure  ,//表示信任策略失敗可以由用戶覆蓋
//        kSecTrustResultFatalTrustFailure  ,//表示信任失敗用戶不能覆蓋
//        kSecTrustResultOtherError
//    }
    
    //iOS系統評估服務器信任
    BOOL isValid = NO;
    SecTrustResultType result;
    //如果評估結果非0則爲驗證失敗,如果評估結果爲0,繼續判斷result
    __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
   //如果result 不是這兩種kSecTrustResultUnspecified 和 kSecTrustResultProceed 則 視爲評估驗證失敗
    isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);

_out:
    return isValid;
}

3:證書鏈

比如在AFNetworking的單元測試中拿到一個證書httpbin_0.cer,在windows操作系統下面打開會看到證書路徑:


此爲證書鏈信息,通過如下代碼得到證書鏈中的證書的公鑰信息:

    NSString *domain = nil;//@"www.httpbin.org";
    SecTrustRef  serverTrust = AFUTTrustChainForCertsInDirectory(nil);
    NSMutableArray *policies = [NSMutableArray array];
    [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];

//    (__bridge_transfer id)SecPolicyCreateBasicX509()
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
    
    CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);

    NSLog(@"%ld",certificateCount);
    for (CFIndex i = 0; i < certificateCount; i++) {
        SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
        
        id publicKey = AFPublicKeyForCertificate((__bridge_transfer NSData *)SecCertificateCopyData(certificate));
        NSLog(@"%@",publicKey);
    }


4:驗證服務信任

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(NSString *)domain
{
    
    //驗證策略爲客戶端本地不做驗證交由系統驗證,或者客戶端本地不制定具體的證書並且允許無效的證書,同時還指定了域名,驗證域名,則視爲驗證不通過
    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
        return NO;
    }

    
    NSMutableArray *policies = [NSMutableArray array];
    //如果要驗證域名的話,就以域名爲參數創建一個 SecPolicyRef,這也是初始化默認設置的狀態
    if (self.validatesDomainName) {
        
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else {
        
        //創建一個符合 X509 標準的默認 SecPolicyRef 對象
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
    }

    //設置服務信任策略
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);

    //下面開始基於設定的策略對證書進行驗證
    
    //客戶端本地不驗證證書交由系統(iOS系統評估)
    if (self.SSLPinningMode == AFSSLPinningModeNone) {
        //允許無效證書則驗證通過,否則如果不允許無效證書交由系統去驗證
        return  self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
    } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
        //如果是系統驗證不通過 serverTrust,並且客戶端不允許無效的證書,本次驗證失敗
        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)];
            }
            //設置serverTrust 的信任錨證書(根據證書鏈的介紹)爲客戶端預置證書
            SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);

            //交由系統評估 serverTrust(實際上是驗證證書鏈)
            if (!AFServerTrustIsValid(serverTrust)) {
                return NO;
            }
         //如果serverTrust 經過系統評估通過,證書鏈驗證通過後,應該包含一個特定的證書(根證書)在證書鏈的最後位置
            
            // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
            //獲取serverTrust中已簽名的證書鏈證書聚合
            NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
            
            //如果客戶端預置證書集合中包含了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);

            //循環比較serverTrust中證書鏈中的公鑰集合和客戶端預置證書的公鑰集合,如果有相同的公鑰,並且公鑰個數大於1就驗證通過
            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;
}

5:獲取服務信任證書鏈中的公鑰集合

//服務信任中的證書鏈的公鑰
static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
    
    //創建符合X509標準的證書安全策略
    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);
       
//        id publicKey = AFPublicKeyForCertificate((__bridge_transfer NSData *)SecCertificateCopyData(certificate));
//        NSLog(@"%@",publicKey);
        
        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];
}

6:獲取服務信任中證書鏈中的證書集合

//服務信任中的證書信任鏈
static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
   
    //得到證書鏈中要評估的證書個數
    CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
    NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];

    for (CFIndex i = 0; i < certificateCount; i++) {
        SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
        [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
    }
   //返回證書鏈中要評估的證書
    return [NSArray arrayWithArray:trustChain];
}






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章