续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 , 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的博大精深,里面还有许多值得深究的知识,如今水平有限不能一一讲解,还需要不断学习,不断交流,有分享有交流才能不断进步,吸取别人先进的知识,学习优秀的代码,一起加油吧,骚年们!