AFNetworking源码之AFSecurityPolicy模块

来源:互联网 发布:淘宝打折网 编辑:程序博客网 时间:2024/05/18 01:18

续AFNetworking综述,NSURLConnection模块,Serialization模块


AFSecurityPolicy


NSURLConnection已经封装了https连接的建立、数据的加密解密功能,我们直接使用NSURLConnection是可以访问 https网站的,但NSURLConnection并没有验证证书是否合法,无法避免中间人攻击。要做到真正安全通讯,需要我们手动去验证服务端返回的证书,AFSecurityPolicy封装了证书验证的过程,让用户可以轻易使用,除了去系统信任CA机构列表验证,还支持SSL  Pinning方式的验证。

AFSecurityPolicy分三种验证模式:

AFSSLPinningModeNone

这个模式表示不做SSL pinning,只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书。若证书是信任机构签发的就会通过,若是自己服务器生成的证书,这里是不会通过的。

AFSSLPinningModeCertificate

这个模式表示用证书绑定方式验证证书,需要客户端保存有服务端的证书拷贝,这里验证分两步,第一步验证证书的域名/有效期等信息,第二步是对比服务端返回的证书跟客户端返回的是否一致。

这里还没弄明白第一步的验证是怎么进行的,代码上跟去系统信任机构列表里验证一样调用了SecTrustEvaluate,只是这里的列表换成了客户端保存的那些证书列表。若要验证这个,是否应该把服务端证书的颁发机构根证书也放到客户端里?

AFSSLPinningModePublicKey

这个模式同样是用证书绑定方式验证,客户端要有服务端的证书拷贝,只是验证时只验证证书里的公钥,不验证证书的有效期等信息。只要公钥是正确的,就能保证通信不会被窃听,因为中间人没有私钥,无法解开通过公钥加密的数据。

详情请见代码:

#import "AFSecurityPolicy.h"// Equivalent of macro in <AssertMacros.h>, without causing compiler warning:// "'DebugAssert' is deprecated: first deprecated in OS X 10.8"//这两个宏方法用于方便地处理调用各种证书方法过程中出现的错误,出现错误后用goto语句直接跳转到结束语//关于为什么要用 __builtin_expect (x, 0) 而不直接用 x != 0,是为了CPU执行时的性能优化,见这里://http://stackoverflow.com/questions/7346929/why-do-we-use-builtin-expect-when-a-straightforward-way-is-to-use-if-else#ifndef AF_Require_noErr       #define AF_Require_noErr(errorCode, exceptionLabel)                        \          do {                                                                    \              if (__builtin_expect(0 != (errorCode), 0)) {                        \                  goto exceptionLabel;                                            \              }                                                                   \          } while (0)#endif#if !defined(__IPHONE_OS_VERSION_MIN_REQUIRED)static NSData * AFSecKeyGetData(SecKeyRef key) {    CFDataRef data = NULL;    AF_Require_noErr(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out);    return (__bridge_transfer NSData *)data;_out:    if (data) {        CFRelease(data);    }    return nil;}#endifstatic BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)    return [(__bridge id)key1 isEqual:(__bridge id)key2];#else    return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)];#endif}//从证书里取public keystatic id AFPublicKeyForCertificate(NSData *certificate) {    id allowedPublicKey = nil;    //取证书SecCertificateRef -> 生成证书数组 -> 生成SecTrustRef -> 从SecTrustRef取PublicKey    SecCertificateRef allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);    SecCertificateRef allowedCertificates[] = {allowedCertificate};    CFArrayRef tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);    SecPolicyRef policy = SecPolicyCreateBasicX509();    SecTrustRef allowedTrust;    AF_Require_noErr(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out);    SecTrustResultType result;    AF_Require_noErr(SecTrustEvaluate(allowedTrust, &result), _out);    allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);_out:    if (allowedTrust) {        CFRelease(allowedTrust);    }    if (policy) {        CFRelease(policy);    }    if (tempCertificates) {        CFRelease(tempCertificates);    }    if (allowedCertificate) {        CFRelease(allowedCertificate);    }    return allowedPublicKey;}static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {    BOOL isValid = NO;    SecTrustResultType result;    AF_Require_noErr(SecTrustEvaluate(serverTrust, &result), _out);    //kSecTrustResultUnspecified:证书通过验证,但用户没有设置这些证书是否被信任    //kSecTrustResultProceed:证书通过验证,用户有操作设置了证书被信任,例如在弹出的是否信任的alert框中选择always trust    isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);_out:    return isValid;}//取出服务端返回的所有证书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];}//取出服务端返回证书里所有的public keystatic NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {    SecPolicyRef policy = SecPolicyCreateBasicX509();    CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);    NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];    //生成证书数组 -> 生成SecTrustRef -> 从SecTrustRef取PublicKey    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;        AF_Require_noErr(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);                SecTrustResultType result;        AF_Require_noErr(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];}#pragma mark -@interface AFSecurityPolicy()@property (readwrite, nonatomic, strong) NSArray *pinnedPublicKeys;@end@implementation AFSecurityPolicy+ (NSArray *)defaultPinnedCertificates {    //默认证书列表,遍历根目录下所有.cer文件    static NSArray *_defaultPinnedCertificates = nil;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        NSBundle *bundle = [NSBundle bundleForClass:[self class]];        NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];        NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[paths count]];        for (NSString *path in paths) {            NSData *certificateData = [NSData dataWithContentsOfFile:path];            [certificates addObject:certificateData];        }        _defaultPinnedCertificates = [[NSArray alloc] initWithArray:certificates];    });    return _defaultPinnedCertificates;}+ (instancetype)defaultPolicy {    AFSecurityPolicy *securityPolicy = [[self alloc] init];    securityPolicy.SSLPinningMode = AFSSLPinningModeNone;    return securityPolicy;}+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {    AFSecurityPolicy *securityPolicy = [[self alloc] init];    securityPolicy.SSLPinningMode = pinningMode;    securityPolicy.validatesDomainName = YES;    [securityPolicy setPinnedCertificates:[self defaultPinnedCertificates]];    return securityPolicy;}- (id)init {    self = [super init];    if (!self) {        return nil;    }    self.validatesCertificateChain = YES;    return self;}#pragma mark -- (void)setPinnedCertificates:(NSArray *)pinnedCertificates {    _pinnedCertificates = pinnedCertificates;    if (self.pinnedCertificates) {        //预先取出public key,用于AFSSLPinningModePublicKey方式的验证        NSMutableArray *mutablePinnedPublicKeys = [NSMutableArray arrayWithCapacity:[self.pinnedCertificates count]];        for (NSData *certificate in self.pinnedCertificates) {            id publicKey = AFPublicKeyForCertificate(certificate);            if (!publicKey) {                continue;            }            [mutablePinnedPublicKeys addObject:publicKey];        }        self.pinnedPublicKeys = [NSArray arrayWithArray:mutablePinnedPublicKeys];    } else {        self.pinnedPublicKeys = nil;    }}#pragma mark -- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust {    return [self evaluateServerTrust:serverTrust forDomain:nil];}- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust                  forDomain:(NSString *)domain{    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);    //向系统内置的根证书验证服务端返回的证书是否合法    //若使用自签名证书,这里的验证是会不合法的,需要设allowInvalidCertificates = YES    if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {        return NO;    }    //取出服务端返回的证书    NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);    switch (self.SSLPinningMode) {        case AFSSLPinningModeNone:            //两种情况走到这里,            //一是通过系统证书验证,返回认证成功            //二是没通过验证,但allowInvalidCertificates = YES,也就是说完全不认证直接返回认证成功            return YES;                    //验证整个证书        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;            }            if (!self.validatesCertificateChain) {                return YES;            }            //整个证书链都跟本地的证书匹配才给过            NSUInteger trustedCertificateCount = 0;            for (NSData *trustChainCertificate in serverCertificates) {                if ([self.pinnedCertificates containsObject:trustChainCertificate]) {                    trustedCertificateCount++;                }            }            return trustedCertificateCount == [serverCertificates count];        }        //只验证证书的public key        case AFSSLPinningModePublicKey: {            NSUInteger trustedPublicKeyCount = 0;            NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);                        //如果不用验证整个证书链,取第一个也就是真正会使用的那个证书验证就行            if (!self.validatesCertificateChain && [publicKeys count] > 0) {                publicKeys = @[[publicKeys firstObject]];            }            //在本地证书里搜索相等的public key,记录找到个数            for (id trustChainPublicKey in publicKeys) {                for (id pinnedPublicKey in self.pinnedPublicKeys) {                    if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {                        trustedPublicKeyCount += 1;                    }                }            }            //验证整个证书链的情况:每个public key都在本地找到算验证通过            //验证单个证书的情况:找到一个算验证通过            return trustedPublicKeyCount > 0 && ((self.validatesCertificateChain && trustedPublicKeyCount == [serverCertificates count]) || (!self.validatesCertificateChain && trustedPublicKeyCount >= 1));        }    }        return NO;}#pragma mark - NSKeyValueObserving+ (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys {    return [NSSet setWithObject:@"pinnedCertificates"];}@end


AFNetworkReachabilityManager 

AFNetworkReachabilityManager - 这个类监控当前网络的可达性,包括检测域名、广域网和 WiFi 接口的可达性,提供回调 block 和 notificaiton,在可达性变化时调用。

相关代码如下:

//三个初始化网络可达监控器对象的方法+ (instancetype)sharedManager;+ (instancetype)managerForDomain:(NSString *)domain;+ (instancetype)managerForAddress:(const struct sockaddr_in *)address;//判断当前网络是否连接- (BOOL)isReachable {    return [self isReachableViaWWAN] || [self isReachableViaWiFi];}//判断当前网络是否连接3G网- (BOOL)isReachableViaWWAN {    return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;}//判断当前网络是否连接WiFi- (BOOL)isReachableViaWiFi {    return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;}// 开启网络监视器,开始检测网络状态的变化- (void)startMonitoring;// 关闭网络监视器,停止检测网络状态的变化- (void)stopMonitoring;// 网络变化时的回调的block- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {    self.networkReachabilityStatusBlock = block;}

AFNetworking的解析就到这里了,只有通过深入的学习,才知道AFNetworking的博大精深,里面还有许多值得深究的知识,如今水平有限不能一一讲解,还需要不断学习,不断交流,有分享有交流才能不断进步,吸取别人先进的知识,学习优秀的代码,一起加油吧,骚年们!

0 0
原创粉丝点击