AFNetworking粗解(AFSecurityPolicy模块)

111178.png

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

AFSecurityPolicy

NSURLConnection已经封装了https连接的建立、数据的加密解密功能,我们直接使用NSURLConnection是可以访问 https网站的,但NSURLConnection并没有验证证书是否合法,无法避免中间人攻击。要做到真正安全通讯,需要我们手动去验证服务端返回的证书,AFSecurityPolicy封装了证书验证的过程,让用户可以轻易使用,除了去系统信任CA机构列表验证,还支持SSL Pinning方式的验证。
AFSecurityPolicy分三种验证模式:
AFSSLPinningModeNone
这个模式表示不做SSL pinning,只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书。若证书是信任机构签发的就会通过,若是自己服务器生成的证书,这里是不会通过的。
AFSSLPinningModeCertificate
这个模式表示用证书绑定方式验证证书,需要客户端保存有服务端的证书拷贝,这里验证分两步,第一步验证证书的域名/有效期等信息,第二步是对比服务端返回的证书跟客户端返回的是否一致。
这里还没弄明白第一步的验证是怎么进行的,代码上跟去系统信任机构列表里验证一样调用了SecTrustEvaluate,只是这里的列表换成了客户端保存的那些证书列表。若要验证这个,是否应该把服务端证书的颁发机构根证书也放到客户端里?
AFSSLPinningModePublicKey
这个模式同样是用证书绑定方式验证,客户端要有服务端的证书拷贝,只是验证时只验证证书里的公钥,不验证证书的有效期等信息。只要公钥是正确的,就能保证通信不会被窃听,因为中间人没有私钥,无法解开通过公钥加密的数据。
详情请见代码:
[objc] view plaincopy

1.  #import "AFSecurityPolicy.h"  
2.    
3.  // Equivalent of macro in <AssertMacros.h>, without causing compiler warning:  
4.  // "'DebugAssert' is deprecated: first deprecated in OS X 10.8"  
5.  //这两个宏方法用于方便地处理调用各种证书方法过程中出现的错误,出现错误后用goto语句直接跳转到结束语  
6.  //关于为什么要用 __builtin_expect (x, 0) 而不直接用 x != 0,是为了CPU执行时的性能优化,见这里:  
7.  //http://stackoverflow.com/questions/7346929/why-do-we-use-builtin-expect-when-a-straightforward-way-is-to-use-if-else  
8.  #ifndef AF_Require_noErr  
9.         #define AF_Require_noErr(errorCode, exceptionLabel)                        \  
10.           do {                                                                    \  
11.               if (__builtin_expect(0 != (errorCode), 0)) {                        \  
12.                   goto exceptionLabel;                                            \  
13.               }                                                                   \  
14.           } while (0)  
15. #endif  
16.   
17. #if !defined(__IPHONE_OS_VERSION_MIN_REQUIRED)  
18. static NSData * AFSecKeyGetData(SecKeyRef key) {  
19.     CFDataRef data = NULL;  
20.   
21.     AF_Require_noErr(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out);  
22.   
23.     return (__bridge_transfer NSData *)data;  
24.   
25. _out:  
26.     if (data) {  
27.         CFRelease(data);  
28.     }  
29.   
30.     return nil;  
31. }  
32. #endif  
33.   
34. static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {  
35. #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)  
36.     return [(__bridge id)key1 isEqual:(__bridge id)key2];  
37. #else  
38.     return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)];  
39. #endif  
40. }  
41.   
42. //从证书里取public key  
43. static id AFPublicKeyForCertificate(NSData *certificate) {  
44.     id allowedPublicKey = nil;  
45.   
46.     //取证书SecCertificateRef -> 生成证书数组 -> 生成SecTrustRef -> 从SecTrustRef取PublicKey  
47.     SecCertificateRef allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);  
48.     SecCertificateRef allowedCertificates[] = {allowedCertificate};  
49.     CFArrayRef tempCertificates = CFArrayCreate(NULL, (const voidvoid **)allowedCertificates, 1, NULL);  
50.   
51.     SecPolicyRef policy = SecPolicyCreateBasicX509();  
52.     SecTrustRef allowedTrust;  
53.     AF_Require_noErr(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out);  
54.   
55.     SecTrustResultType result;  
56.     AF_Require_noErr(SecTrustEvaluate(allowedTrust, &result), _out);  
57.   
58.     allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);  
59.   
60. _out:  
61.     if (allowedTrust) {  
62.         CFRelease(allowedTrust);  
63.     }  
64.   
65.     if (policy) {  
66.         CFRelease(policy);  
67.     }  
68.   
69.     if (tempCertificates) {  
70.         CFRelease(tempCertificates);  
71.     }  
72.   
73.     if (allowedCertificate) {  
74.         CFRelease(allowedCertificate);  
75.     }  
76.   
77.     return allowedPublicKey;  
78. }  
79.   
80. static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {  
81.     BOOL isValid = NO;  
82.     SecTrustResultType result;  
83.     AF_Require_noErr(SecTrustEvaluate(serverTrust, &result), _out);  
84.   
85.     //kSecTrustResultUnspecified:证书通过验证,但用户没有设置这些证书是否被信任  
86.     //kSecTrustResultProceed:证书通过验证,用户有操作设置了证书被信任,例如在弹出的是否信任的alert框中选择always trust  
87.     isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);  
88.   
89. _out:  
90.     return isValid;  
91. }  
92.   
93. //取出服务端返回的所有证书  
94. static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {  
95.     CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);  
96.     NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];  
97.   
98.     for (CFIndex i = 0; i < certificateCount; i++) {  
99.         SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);  
100.            [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];  
101.        }  
102.      
103.        return [NSArray arrayWithArray:trustChain];  
104.    }  
105.      
106.    //取出服务端返回证书里所有的public key  
107.    static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {  
108.        SecPolicyRef policy = SecPolicyCreateBasicX509();  
109.        CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);  
110.        NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];  
111.        //生成证书数组 -> 生成SecTrustRef -> 从SecTrustRef取PublicKey  
112.        for (CFIndex i = 0; i < certificateCount; i++) {  
113.            SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);  
114.      
115.            SecCertificateRef someCertificates[] = {certificate};  
116.            CFArrayRef certificates = CFArrayCreate(NULL, (const voidvoid **)someCertificates, 1, NULL);  
117.      
118.            SecTrustRef trust;  
119.            AF_Require_noErr(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);  
120.              
121.            SecTrustResultType result;  
122.            AF_Require_noErr(SecTrustEvaluate(trust, &result), _out);  
123.      
124.            [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];  
125.      
126.        _out:  
127.            if (trust) {  
128.                CFRelease(trust);  
129.            }  
130.      
131.            if (certificates) {  
132.                CFRelease(certificates);  
133.            }  
134.      
135.            continue;  
136.        }  
137.        CFRelease(policy);  
138.      
139.        return [NSArray arrayWithArray:trustChain];  
140.    }  
141.      
142.    #pragma mark -  
143.      
144.    @interface AFSecurityPolicy()  
145.    @property (readwrite, nonatomic, strong) NSArray *pinnedPublicKeys;  
146.    @end  
147.      
148.    @implementation AFSecurityPolicy  
149.      
150.    + (NSArray *)defaultPinnedCertificates {  
151.        //默认证书列表,遍历根目录下所有.cer文件  
152.        static NSArray *_defaultPinnedCertificates = nil;  
153.        static dispatch_once_t onceToken;  
154.        dispatch_once(&onceToken, ^{  
155.            NSBundle *bundle = [NSBundle bundleForClass:[self class]];  
156.            NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];  
157.      
158.            NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[paths count]];  
159.            for (NSString *path in paths) {  
160.                NSData *certificateData = [NSData dataWithContentsOfFile:path];  
161.                [certificates addObject:certificateData];  
162.            }  
163.      
164.            _defaultPinnedCertificates = [[NSArray alloc] initWithArray:certificates];  
165.        });  
166.      
167.        return _defaultPinnedCertificates;  
168.    }  
169.      
170.    + (instancetype)defaultPolicy {  
171.        AFSecurityPolicy *securityPolicy = [[self alloc] init];  
172.        securityPolicy.SSLPinningMode = AFSSLPinningModeNone;  
173.      
174.        return securityPolicy;  
175.    }  
176.      
177.    + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {  
178.        AFSecurityPolicy *securityPolicy = [[self alloc] init];  
179.        securityPolicy.SSLPinningMode = pinningMode;  
180.        securityPolicy.validatesDomainName = YES;  
181.        [securityPolicy setPinnedCertificates:[self defaultPinnedCertificates]];  
182.      
183.        return securityPolicy;  
184.    }  
185.      
186.    - (id)init {  
187.        self = [super init];  
188.        if (!self) {  
189.            return nil;  
190.        }  
191.      
192.        self.validatesCertificateChain = YES;  
193.      
194.        return self;  
195.    }  
196.      
197.    #pragma mark -  
198.      
199.    - (void)setPinnedCertificates:(NSArray *)pinnedCertificates {  
200.        _pinnedCertificates = pinnedCertificates;  
201.      
202.        if (self.pinnedCertificates) {  
203.            //预先取出public key,用于AFSSLPinningModePublicKey方式的验证  
204.            NSMutableArray *mutablePinnedPublicKeys = [NSMutableArray arrayWithCapacity:[self.pinnedCertificates count]];  
205.            for (NSData *certificate in self.pinnedCertificates) {  
206.                id publicKey = AFPublicKeyForCertificate(certificate);  
207.                if (!publicKey) {  
208.                    continue;  
209.                }  
210.                [mutablePinnedPublicKeys addObject:publicKey];  
211.            }  
212.            self.pinnedPublicKeys = [NSArray arrayWithArray:mutablePinnedPublicKeys];  
213.        } else {  
214.            self.pinnedPublicKeys = nil;  
215.        }  
216.    }  
217.      
218.    #pragma mark -  
219.      
220.    - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust {  
221.        return [self evaluateServerTrust:serverTrust forDomain:nil];  
222.    }  
223.      
224.    - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust  
225.                      forDomain:(NSString *)domain  
226.    {  
227.        NSMutableArray *policies = [NSMutableArray array];  
228.        if (self.validatesDomainName) {  
229.            [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];  
230.        } else {  
231.            [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];  
232.        }  
233.      
234.        SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);  
235.      
236.        //向系统内置的根证书验证服务端返回的证书是否合法  
237.        //若使用自签名证书,这里的验证是会不合法的,需要设allowInvalidCertificates = YES  
238.        if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {  
239.            return NO;  
240.        }  
241.      
242.        //取出服务端返回的证书  
243.        NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);  
244.        switch (self.SSLPinningMode) {  
245.            case AFSSLPinningModeNone:  
246.                //两种情况走到这里,  
247.                //一是通过系统证书验证,返回认证成功  
248.                //二是没通过验证,但allowInvalidCertificates = YES,也就是说完全不认证直接返回认证成功  
249.                return YES;  
250.                  
251.            //验证整个证书  
252.            case AFSSLPinningModeCertificate: {  
253.                NSMutableArray *pinnedCertificates = [NSMutableArray array];  
254.                for (NSData *certificateData in self.pinnedCertificates) {  
255.                    [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];  
256.                }  
257.                //在本地证书里验证服务端返回的证书的有效性  
258.                SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);  
259.      
260.                if (!AFServerTrustIsValid(serverTrust)) {  
261.                    return NO;  
262.                }  
263.      
264.                if (!self.validatesCertificateChain) {  
265.                    return YES;  
266.                }  
267.      
268.                //整个证书链都跟本地的证书匹配才给过  
269.                NSUInteger trustedCertificateCount = 0;  
270.                for (NSData *trustChainCertificate in serverCertificates) {  
271.                    if ([self.pinnedCertificates containsObject:trustChainCertificate]) {  
272.                        trustedCertificateCount++;  
273.                    }  
274.                }  
275.      
276.                return trustedCertificateCount == [serverCertificates count];  
277.            }  
278.            //只验证证书的public key  
279.            case AFSSLPinningModePublicKey: {  
280.                NSUInteger trustedPublicKeyCount = 0;  
281.                NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);  
282.                  
283.                //如果不用验证整个证书链,取第一个也就是真正会使用的那个证书验证就行  
284.                if (!self.validatesCertificateChain && [publicKeys count] > 0) {  
285.                    publicKeys = @[[publicKeys firstObject]];  
286.                }  
287.      
288.                //在本地证书里搜索相等的public key,记录找到个数  
289.                for (id trustChainPublicKey in publicKeys) {  
290.                    for (id pinnedPublicKey in self.pinnedPublicKeys) {  
291.                        if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {  
292.                            trustedPublicKeyCount += 1;  
293.                        }  
294.                    }  
295.                }  
296.      
297.                //验证整个证书链的情况:每个public key都在本地找到算验证通过  
298.                //验证单个证书的情况:找到一个算验证通过  
299.                return trustedPublicKeyCount > 0 && ((self.validatesCertificateChain && trustedPublicKeyCount == [serverCertificates count]) || (!self.validatesCertificateChain && trustedPublicKeyCount >= 1));  
300.            }  
301.        }  
302.          
303.        return NO;  
304.    }  
305.      
306.    #pragma mark - NSKeyValueObserving  
307.      
308.    + (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys {  
309.        return [NSSet setWithObject:@"pinnedCertificates"];  
310.    }  
311.      
312.    @end  

AFNetworkReachabilityManager
AFNetworkReachabilityManager - 这个类监控当前网络的可达性,包括检测域名、广域网和 WiFi 接口的可达性,提供回调 block 和 notificaiton,在可达性变化时调用。
相关代码如下:
[objc] view plaincopy

1.  //三个初始化网络可达监控器对象的方法  
2.  + (instancetype)sharedManager;  
3.  + (instancetype)managerForDomain:(NSString *)domain;  
4.  + (instancetype)managerForAddress:(const struct sockaddr_in *)address;  
5.  //判断当前网络是否连接  
6.  - (BOOL)isReachable {  
7.      return [self isReachableViaWWAN] || [self isReachableViaWiFi];  
8.  }  
9.  //判断当前网络是否连接3G网  
10. - (BOOL)isReachableViaWWAN {  
11.     return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;  
12. }  
13. //判断当前网络是否连接WiFi  
14. - (BOOL)isReachableViaWiFi {  
15.     return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;  
16. }  
17. // 开启网络监视器,开始检测网络状态的变化  
18. - (void)startMonitoring;  
19. // 关闭网络监视器,停止检测网络状态的变化  
20. - (void)stopMonitoring;  
21. // 网络变化时的回调的block  
22. - (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {  
23.     self.networkReachabilityStatusBlock = block;  
24. }  

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容