
續(xù)AFNetworking綜述,NSURLConnection模塊,Serialization模塊
AFSecurityPolicy
NSURLConnection已經(jīng)封裝了https連接的建立、數(shù)據(jù)的加密解密功能,我們直接使用NSURLConnection是可以訪問 https網(wǎng)站的,但NSURLConnection并沒有驗證證書是否合法,無法避免中間人攻擊。要做到真正安全通訊,需要我們手動去驗證服務端返回的證書,AFSecurityPolicy封裝了證書驗證的過程,讓用戶可以輕易使用,除了去系統(tǒng)信任CA機構列表驗證,還支持SSL Pinning方式的驗證。
AFSecurityPolicy分三種驗證模式:
AFSSLPinningModeNone
這個模式表示不做SSL pinning,只跟瀏覽器一樣在系統(tǒng)的信任機構列表里驗證服務端返回的證書。若證書是信任機構簽發(fā)的就會通過,若是自己服務器生成的證書,這里是不會通過的。
AFSSLPinningModeCertificate
這個模式表示用證書綁定方式驗證證書,需要客戶端保存有服務端的證書拷貝,這里驗證分兩步,第一步驗證證書的域名/有效期等信息,第二步是對比服務端返回的證書跟客戶端返回的是否一致。
這里還沒弄明白第一步的驗證是怎么進行的,代碼上跟去系統(tǒng)信任機構列表里驗證一樣調用了SecTrustEvaluate,只是這里的列表換成了客戶端保存的那些證書列表。若要驗證這個,是否應該把服務端證書的頒發(fā)機構根證書也放到客戶端里?
AFSSLPinningModePublicKey
這個模式同樣是用證書綁定方式驗證,客戶端要有服務端的證書拷貝,只是驗證時只驗證證書里的公鑰,不驗證證書的有效期等信息。只要公鑰是正確的,就能保證通信不會被竊聽,因為中間人沒有私鑰,無法解開通過公鑰加密的數(shù)據(jù)。
詳情請見代碼:
[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. //這兩個宏方法用于方便地處理調用各種證書方法過程中出現(xiàn)的錯誤,出現(xiàn)錯誤后用goto語句直接跳轉到結束語
6. //關于為什么要用 __builtin_expect (x, 0) 而不直接用 x != 0,是為了CPU執(zhí)行時的性能優(yōu)化,見這里:
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 -> 生成證書數(shù)組 -> 生成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. //生成證書數(shù)組 -> 生成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. //向系統(tǒng)內置的根證書驗證服務端返回的證書是否合法
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. //一是通過系統(tǒng)證書驗證,返回認證成功
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,記錄找到個數(shù)
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 - 這個類監(jiān)控當前網(wǎng)絡的可達性,包括檢測域名、廣域網(wǎng)和 WiFi 接口的可達性,提供回調 block 和 notificaiton,在可達性變化時調用。
相關代碼如下:
[objc] view plaincopy
1. //三個初始化網(wǎng)絡可達監(jiān)控器對象的方法
2. + (instancetype)sharedManager;
3. + (instancetype)managerForDomain:(NSString *)domain;
4. + (instancetype)managerForAddress:(const struct sockaddr_in *)address;
5. //判斷當前網(wǎng)絡是否連接
6. - (BOOL)isReachable {
7. return [self isReachableViaWWAN] || [self isReachableViaWiFi];
8. }
9. //判斷當前網(wǎng)絡是否連接3G網(wǎng)
10. - (BOOL)isReachableViaWWAN {
11. return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;
12. }
13. //判斷當前網(wǎng)絡是否連接WiFi
14. - (BOOL)isReachableViaWiFi {
15. return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;
16. }
17. // 開啟網(wǎng)絡監(jiān)視器,開始檢測網(wǎng)絡狀態(tài)的變化
18. - (void)startMonitoring;
19. // 關閉網(wǎng)絡監(jiān)視器,停止檢測網(wǎng)絡狀態(tài)的變化
20. - (void)stopMonitoring;
21. // 網(wǎng)絡變化時的回調的block
22. - (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
23. self.networkReachabilityStatusBlock = block;
24. }
AFNetworking的解析就到這里了,只有通過深入的學習,才知道AFNetworking的博大精深,里面還有許多值得深究的知識,如今水平有限不能一一講解,還需要不斷學習,不斷交流,有分享有交流才能不斷進步,吸取別人先進的知識,學習優(yōu)秀的代碼,一起加油吧,騷年們!