原文地址:http://blog.5ibc.net/p/101504.htmlhtml
首先,須要明確你使用HTTP/HTTPS的用途,由於OSX和iOS平臺提供了多種API,來支持不一樣的用途,官方文檔《Making HTTP and HTTPS Requests》有詳細的說明,而文檔《HTTPS Server Trust Evaluation》則詳細講解了HTTPS驗證相關知識,這裏就很少說了。本文主要講解咱們最經常使用的NSURLConnection支持HTTPS的實現(NSURLSession的實現方法相似,只是要求受權證實的回調不同而已),以及怎麼樣使用AFNetworking這個很是流行的第三方庫來支持HTTPS。本文假設你對HTTP以及NSURLConnection的接口有了足夠的瞭解。安全
1. 驗證證書的API服務器
相關的Api在Security Framework中,驗證流程以下:學習
1). 第一步,先獲取須要驗證的信任對象(Trust Object)。這個Trust Object在不一樣的應用場景下獲取的方式都不同,對於NSURLConnection來講,是從delegate方法-connection:willSendRequestForAuthenticationChallenge:回調回來的參數challenge中獲取([challenge.protectionSpace serverTrust])。google
2). 使用系統默認驗證方式驗證Trust Object。SecTrustEvaluate會根據Trust Object的驗證策略,一級一級往上,驗證證書鏈上每一級數字簽名的有效性(上一部分有講解),從而評估證書的有效性。lua
3). 如第二步驗證經過了,通常的安全要求下,就能夠直接驗證經過,進入到下一步:使用Trust Object生成一份憑證([NSURLCredential credentialForTrust:serverTrust]),傳入challenge的sender中([challenge.sender useCredential:cred forAuthenticationChallenge:challenge])處理,創建鏈接。url
4). 假若有更強的安全要求,能夠繼續對Trust Object進行更嚴格的驗證。經常使用的方式是在本地導入證書,驗證Trust Object與導入的證書是否匹配。更多的方法能夠查看Enforcing Stricter Server Trust Evaluation,這一部分在講解AFNetworking源碼中會講解到。spa
5). 假如驗證失敗,取消這次Challenge-Response Authentication驗證流程,拒絕鏈接請求。操作系統
ps: 假如是自建證書的,則會跳過第二步,使用第三部進行驗證,由於自建證書的根CA的數字簽名未在操做系統的信任列表中。.net
iOS受權驗證的API和流程大概瞭解了,下面,咱們看看在NSURLConnection中的代碼實現:
使用NSURLConnection支持HTTPS的實現
// Now start the connection NSURL * httpsURL = [NSURL URLWithString:@"https://www.google.com"]; self.connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:httpsURL] delegate:self]; //回調 - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { //1)獲取trust object SecTrustRef trust = challenge.protectionSpace.serverTrust; SecTrustResultType result; //2)SecTrustEvaluate對trust進行驗證 OSStatus status = SecTrustEvaluate(trust, &result); if (status == errSecSuccess && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)) { //3)驗證成功,生成NSURLCredential憑證cred,告知challenge的sender使用這個憑證來繼續鏈接 NSURLCredential *cred = [NSURLCredential credentialForTrust:trust]; [challenge.sender useCredential:cred forAuthenticationChallenge:challenge]; } else { //5)驗證失敗,取消此次驗證流程 [challenge.sender cancelAuthenticationChallenge:challenge]; } }
上面是代碼是經過系統默認驗證流程來驗證證書的。假如咱們是自建證書的呢?這樣Trust Object裏面服務器的證書由於不是可信任的CA簽發的,因此直接使用SecTrustEvaluate進行驗證是不會成功。又或者,即便服務器返回的證書是信任CA簽發的,又如何肯定這證書就是咱們想要的特定證書?這就須要先在本地導入證書,設置成須要驗證的Anchor Certificate(就是根證書),再調用SecTrustEvaluate來驗證。代碼以下
//先導入證書 NSString * cerPath = ...; //證書的路徑 NSData * cerData = [NSData dataWithContentsOfFile:cerPath]; SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData)); self.trustedCertificates = @[CFBridgingRelease(certificate)]; //回調 - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { //1)獲取trust object SecTrustRef trust = challenge.protectionSpace.serverTrust; SecTrustResultType result; //注意:這裏將以前導入的證書設置成下面驗證的Trust Object的anchor certificate SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)self.trustedCertificates); //2)SecTrustEvaluate會查找前面SecTrustSetAnchorCertificates設置的證書或者系統默認提供的證書,對trust進行驗證 OSStatus status = SecTrustEvaluate(trust, &result); if (status == errSecSuccess && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)) { //3)驗證成功,生成NSURLCredential憑證cred,告知challenge的sender使用這個憑證來繼續鏈接 NSURLCredential *cred = [NSURLCredential credentialForTrust:trust]; [challenge.sender useCredential:cred forAuthenticationChallenge:challenge]; } else { //5)驗證失敗,取消此次驗證流程 [challenge.sender cancelAuthenticationChallenge:challenge]; } }
建議採用本地導入證書的方式驗證證書,來保證足夠的安全性。更多的驗證方法,請查看官方文檔《HTTPS Server Trust Evaluation》
2. 使用AFNetworking來支持HTTPS
AFNetworking是iOS/OSX開發最流行的第三方開源庫之一,其做者是很是著名的iOS/OSX開發者Mattt Thompson,其博客NSHipster也是iOS/OSX開發者學習和開闊技術視野的好地方。AFNetworking已經將上面的邏輯代碼封裝好,甚至更完善,在AFSecurityPolicy文件中,有興趣能夠閱讀這個模塊的代碼;
AFNetworking上配置對HTTPS的支持很是簡單:
NSURL * url = [NSURL URLWithString:@"https://www.google.com"]; AFHTTPRequestOperationManager * requestOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:url]; dispatch_queue_t requestQueue = dispatch_create_serial_queue_for_name("kRequestCompletionQueue"); requestOperationManager.completionQueue = requestQueue; AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; //allowInvalidCertificates 是否容許無效證書(也就是自建的證書),默認爲NO //若是是須要驗證自建證書,須要設置爲YES securityPolicy.allowInvalidCertificates = YES; //validatesDomainName 是否須要驗證域名,默認爲YES; //假如證書的域名與你請求的域名不一致,需把該項設置爲NO //主要用於這種狀況:客戶端請求的是子域名,而證書上的是另一個域名。由於SSL證書上的域名是獨立的,假如證書上註冊的域名是www.google.com,那麼mail.google.com是沒法驗證經過的;固然,有錢能夠註冊通配符的域名*.google.com,但這個仍是比較貴的。 securityPolicy.validatesDomainName = NO; //validatesCertificateChain 是否驗證整個證書鏈,默認爲YES //設置爲YES,會將服務器返回的Trust Object上的證書鏈與本地導入的證書進行對比,這就意味着,假如你的證書鏈是這樣的: //GeoTrust Global CA // Google Internet Authority G2 // *.google.com //那麼,除了導入*.google.com以外,還須要導入證書鏈上全部的CA證書(GeoTrust Global CA, Google Internet Authority G2); //如是自建證書的時候,能夠設置爲YES,加強安全性;假如是信任的CA所簽發的證書,則建議關閉該驗證; securityPolicy.validatesCertificateChain = NO; requestOperationManager.securityPolicy = securityPolicy;
這就是AFNetworking的支持HTTPS的主要配置說明,AFHTTPSessionManager與之基本一致,就不重複了。
3. 總結
從2017年1月起,iOS必須支持HTTPS,因此HTTPS已是大勢所趨了。