AFNetworking粗解(AFSecurityPolicy模塊)

111178.png

續(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)秀的代碼,一起加油吧,騷年們!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容