iOS开发HTTPS实现之信任SSL证书和自签名证书
首先來分析一下什么是HTTPS以及了解HTTPS對于iOS開發(fā)者的意義
HTTPS 以及SSL/TSL
什么是SSL?
SSL(Secure Sockets Layer, 安全套接字層),因為原先互聯(lián)網(wǎng)上使用的 HTTP 協(xié)議是明文的,存在很多缺點,比如傳輸內(nèi)容會被偷窺(嗅探)和篡改。 SSL 協(xié)議的作用就是在傳輸層對網(wǎng)絡(luò)連接進行加密。
何為TLS?
到了1999年,SSL 因為應(yīng)用廣泛,已經(jīng)成為互聯(lián)網(wǎng)上的事實標(biāo)準(zhǔn)。IETF 就在那年把 SSL 標(biāo)準(zhǔn)化。標(biāo)準(zhǔn)化之后的名稱改為 TLS(Transport Layer Security,傳輸層安全協(xié)議)。SSL與TLS可以視作同一個東西的不同階段
HTTPS
簡單來說,HTTPS = HTTP + SSL/TLS, 也就是 HTTP over SSL 或 HTTP over TLS,這是后面加 S 的由來 。
HTTPS和HTTP異同:HTTP和HTTPS使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。HTTP的連接很簡單,是無狀態(tài)的;HTTPS協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進行加密傳輸、身份認證的網(wǎng)絡(luò)協(xié)議,比HTTP協(xié)議安全。
在WWDC 2016開發(fā)者大會上,蘋果宣布了一個最后期限:到2017年1月1日 App Store中的所有應(yīng)用都必須啟用 App Transport Security安全功能。App Transport Security(ATS)是蘋果在iOS 9中引入的一項隱私保護功能,屏蔽明文HTTP資源加載,連接必須經(jīng)過更安全的HTTPS。蘋果目前允許開發(fā)者暫時關(guān)閉ATS,可以繼續(xù)使用HTTP連接,但到年底所有官方商店的應(yīng)用都必須強制性使用ATS。
所以對于iOS開發(fā)者來說,需要盡早解決HTTPS請求的問題。
發(fā)送HTTPS請求信任SSL證書和自簽名證書,分為三種情況
1.如果你的app服務(wù)端安裝的是SLL頒發(fā)的CA,可以使用系統(tǒng)方法直接實現(xiàn)信任SSL證書,關(guān)于Apple對SSL證書的要求請參考:蘋果官方文檔CertKeyTrustProgGuide
這種方式不需要在Bundle中引入CA文件,可以交給系統(tǒng)去判斷服務(wù)器端的證書是不是SSL證書,驗證過程也不需要我們?nèi)ゾ唧w實現(xiàn)。
示例代碼:
NSURL?*URL?=?[NSURL?URLWithString:URLString];????NSURLRequest?*request?=?[[NSURLRequest?alloc]?initWithURL:URL?cachePolicy:NSURLRequestReloadIgnoringLocalCacheData?timeoutInterval:10];????//創(chuàng)建同步連接NSError?*error?=?nil;????NSData?*receivedData?=?[NSURLConnection?sendSynchronousRequest:request?returningResponse:nil?error:&error];????NSString?*receivedInfo?=?[[NSString?alloc]?initWithData:receivedData?encoding:NSUTF8StringEncoding];當(dāng)然,如果你需要同時信任SSL證書和自簽名證書的話還是需要在代碼中實現(xiàn)CA的驗證,這種情況在后面會提到。
2.基于AFNetWorking的SSL特定服務(wù)器證書信任處理,重寫AFNetWorking的customSecurityPolicy方法,這里我創(chuàng)建了一個HttpRequest類,分別對GET和POST方法進行了封裝,以GET方法為例:
+?(void)get:(NSString?*)url?params:(NSDictionary?*)params?success:(void?(^)(id))success?failure:(void?(^)(NSError?*))failure?{????//?1.獲得請求管理者AFHTTPRequestOperationManager?*mgr?=?[AFHTTPRequestOperationManager?manager];????//?2.申明返回的結(jié)果是text/html類型mgr.responseSerializer?=?[AFHTTPResponseSerializer?serializer];????//?3.設(shè)置超時時間為10smgr.requestSerializer.timeoutInterval?=?10;????//?加上這行代碼,https?ssl?驗證。if(openHttpsSSL)?{[mgr?setSecurityPolicy:[self?customSecurityPolicy]];}????//?4.發(fā)送GET請求[mgr?GET:url?parameters:params?success:^(AFHTTPRequestOperation?*operation,?id?responseObj){????????if?(success)?{success(responseObj);}}?failure:^(AFHTTPRequestOperation?*operation,?NSError?*error)?{????????if?(error)?{failure(error);}}]; }+?(AFSecurityPolicy*)customSecurityPolicy?{????//?/先導(dǎo)入證書NSString?*cerPath?=?[[NSBundle?mainBundle]?pathForResource:certificate?ofType:@"cer"];//證書的路徑NSData?*certData?=?[NSData?dataWithContentsOfFile:cerPath];????//?AFSSLPinningModeCertificate?使用證書驗證模式AFSecurityPolicy?*securityPolicy?=?[AFSecurityPolicy?policyWithPinningMode:AFSSLPinningModeCertificate];????//?allowInvalidCertificates?是否允許無效證書(也就是自建的證書),默認為NO//?如果是需要驗證自建證書,需要設(shè)置為YESsecurityPolicy.allowInvalidCertificates?=?YES;????//validatesDomainName?是否需要驗證域名,默認為YES;//假如證書的域名與你請求的域名不一致,需把該項設(shè)置為NO;如設(shè)成NO的話,即服務(wù)器使用其他可信任機構(gòu)頒發(fā)的證書,也可以建立連接,這個非常危險,建議打開。//置為NO,主要用于這種情況:客戶端請求的是子域名,而證書上的是另外一個域名。因為SSL證書上的域名是獨立的,假如證書上注冊的域名是www.google.com,那么mail.google.com是無法驗證通過的;當(dāng)然,有錢可以注冊通配符的域名*.google.com,但這個還是比較貴的。//如置為NO,建議自己添加對應(yīng)域名的校驗邏輯。securityPolicy.validatesDomainName?=?NO;securityPolicy.pinnedCertificates?=?@[certData];????return?securityPolicy; }其中的cerPath就是app bundle中證書路徑,certificate為證書名稱的宏,僅支持cer格式,securityPolicy的相關(guān)配置尤為重要,請仔細閱讀customSecurityPolicy方法并根據(jù)實際情況設(shè)置其屬性。
這樣,就能夠在AFNetWorking的基礎(chǔ)上使用HTTPS協(xié)議訪問特定服務(wù)器,但是不能信任根證書的CA文件,因此這種方式存在風(fēng)險,讀取pinnedCertificates中的證書數(shù)組的時候有可能失敗,如果證書不符合,certData就會為nil。
3.更改系統(tǒng)方法,發(fā)送異步NSURLConnection請求。
-?(void)getDataWithURLRequest?{????//connectionNSString?*urlStr?=?@"https://developer.apple.com/cn/";????NSURL?*url?=?[NSURL?URLWithString:urlStr];????NSMutableURLRequest?*request?=?[NSMutableURLRequest?requestWithURL:url?cachePolicy:NSURLRequestUseProtocolCachePolicy?timeoutInterval:10];????NSURLConnection?*connection?=?[[NSURLConnection?alloc]initWithRequest:request?delegate:self];[connection?start]; }重點在于處理NSURLConnection的didReceiveAuthenticationChallenge代理方法,對CA文件進行驗證,并建立信任連接。
-?(BOOL)connection:(NSURLConnection?*)connection?canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace?*)protectionSpace?{????return?[protectionSpace.authenticationMethod?isEqualToString:NSURLAuthenticationMethodServerTrust]; }-?(void)connection:(NSURLConnection?*)connection?didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge?*)challenge?{?/*//直接驗證服務(wù)器是否被認證(serverTrust),這種方式直接忽略證書驗證,直接建立連接,但不能過濾其它URL連接,可以理解為一種折衷的處理方式,實際上并不安全,因此不推薦。SecTrustRef?serverTrust?=?[[challenge?protectionSpace]?serverTrust];return?[[challenge?sender]?useCredential:?[NSURLCredential?credentialForTrust:?serverTrust]forAuthenticationChallenge:?challenge];*/if?([[[challenge?protectionSpace]?authenticationMethod]?isEqualToString:?NSURLAuthenticationMethodServerTrust])?{????????do{SecTrustRef?serverTrust?=?[[challenge?protectionSpace]?serverTrust];????????????NSCAssert(serverTrust?!=?nil,?@"serverTrust?is?nil");????????????if(nil?==?serverTrust)????????????????break;?/*?failed?*//***??導(dǎo)入多張CA證書(Certification?Authority,支持SSL證書以及自簽名的CA)*/NSString?*cerPath?=?[[NSBundle?mainBundle]?pathForResource:@"cloudwin"?ofType:@"cer"];//自簽名證書NSData*?caCert?=?[NSData?dataWithContentsOfFile:cerPath];????????????NSString?*cerPath2?=?[[NSBundle?mainBundle]?pathForResource:@"apple"?ofType:@"cer"];//SSL證書NSData?*?caCert2?=?[NSData?dataWithContentsOfFile:cerPath2];????????????NSCAssert(caCert?!=?nil,?@"caCert?is?nil");????????????if(nil?==?caCert)????????????????break;?/*?failed?*/NSCAssert(caCert2?!=?nil,?@"caCert2?is?nil");????????????if?(nil?==?caCert2)?{????????????????break;}SecCertificateRef?caRef?=?SecCertificateCreateWithData(NULL,?(__bridge?CFDataRef)caCert);????????????NSCAssert(caRef?!=?nil,?@"caRef?is?nil");????????????if(nil?==?caRef)????????????????break;?/*?failed?*/SecCertificateRef?caRef2?=?SecCertificateCreateWithData(NULL,?(__bridge?CFDataRef)caCert2);????????????NSCAssert(caRef2?!=?nil,?@"caRef2?is?nil");????????????if(nil?==?caRef2)????????????????break;?/*?failed?*/NSArray?*caArray?=?@[(__bridge?id)(caRef),(__bridge?id)(caRef2)];????????????NSCAssert(caArray?!=?nil,?@"caArray?is?nil");????????????if(nil?==?caArray)????????????????break;?/*?failed?*/OSStatus?status?=?SecTrustSetAnchorCertificates(serverTrust,?(__bridge?CFArrayRef)caArray);????????????NSCAssert(errSecSuccess?==?status,?@"SecTrustSetAnchorCertificates?failed");????????????if(!(errSecSuccess?==?status))????????????????break;?/*?failed?*/SecTrustResultType?result?=?-1;status?=?SecTrustEvaluate(serverTrust,?&result);????????????if(!(errSecSuccess?==?status))????????????????break;?/*?failed?*/NSLog(@"stutas:%d",(int)status);????????????NSLog(@"Result:?%d",?result);????????????BOOL?allowConnect?=?(result?==?kSecTrustResultUnspecified)?||?(result?==?kSecTrustResultProceed);????????????if?(allowConnect)?{????????????????NSLog(@"success");}else?{????????????????NSLog(@"error");}????????????/*?https://developer.apple.com/library/ios/technotes/tn2232/_index.html?*//*?https://developer.apple.com/library/mac/qa/qa1360/_index.html?*//*?kSecTrustResultUnspecified?and?kSecTrustResultProceed?are?success?*/if(!?allowConnect){????????????break;?/*?failed?*/}#if?0/*?Treat?kSecTrustResultConfirm?and?kSecTrustResultRecoverableTrustFailure?as?success?*//*???since?the?user?will?likely?tap-through?to?see?the?dancing?bunnies?*/if(result?==?kSecTrustResultDeny?||?result?==?kSecTrustResultFatalTrustFailure?||?result?==?kSecTrustResultOtherError)????????????????break;?/*?failed?to?trust?cert?(good?in?this?case)?*/#endif//?The?only?good?exit?pointreturn?[[challenge?sender]?useCredential:?[NSURLCredential?credentialForTrust:?serverTrust]forAuthenticationChallenge:?challenge];}?while(0);}????//?Bad?dogreturn?[[challenge?sender]?cancelAuthenticationChallenge:?challenge];}這里的關(guān)鍵在于result參數(shù)的值,根據(jù)官方文檔的說明,判斷(result == kSecTrustResultUnspecified) || (result == kSecTrustResultProceed)的值,若為1,則該網(wǎng)站的CA被app信任成功,可以建立數(shù)據(jù)連接,這意味著所有由該CA簽發(fā)的各個服務(wù)器證書都被信任,而訪問其它沒有被信任的任何網(wǎng)站都會連接失敗。該CA文件既可以是SLL也可以是自簽名。
NSURLConnection的其它代理方法實現(xiàn)
#pragma?mark?--?connect的異步代理方法-(void)connection:(NSURLConnection?*)connection?didReceiveResponse:(NSURLResponse?*)response?{????NSLog(@"請求被響應(yīng)");_mData?=?[[NSMutableData?alloc]init]; }-(void)connection:(NSURLConnection?*)connection?didReceiveData:(nonnull?NSData?*)data?{????NSLog(@"開始返回數(shù)據(jù)片段");[_mData?appendData:data]; }-(void)connectionDidFinishLoading:(NSURLConnection?*)connection?{????NSLog(@"鏈接完成");????//可以在此解析數(shù)據(jù)NSString?*receiveInfo?=?[NSJSONSerialization?JSONObjectWithData:self.mData?options:NSJSONReadingAllowFragments?error:nil];????NSLog(@"received?data:\\\\n%@",self.mData);????NSLog(@"received?info:\\\\n%@",receiveInfo); }//鏈接出錯-(void)connection:(NSURLConnection?*)connection?didFailWithError:(NSError?*)error?{????NSLog(@"error?-?%@",error); }至此,HTTPS信任證書的問題得以解決,這不僅是為了響應(yīng)Apple強制性使用ATS的要求,也是為了實際生產(chǎn)環(huán)境安全性的考慮,HTTPS是未來的趨勢,建議盡早支持。
如需參考Demo請移步本人在Github上的開源項目
文/無忌不悔(簡書作者)
原文鏈接:http://www.jianshu.com/p/6b9c8bd5005a#
著作權(quán)歸作者所有,轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),并標(biāo)注“簡書作者”。
轉(zhuǎn)載于:https://blog.51cto.com/ljianbing/1874196
總結(jié)
以上是生活随笔為你收集整理的iOS开发HTTPS实现之信任SSL证书和自签名证书的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [转]优秀Python学习资源收集汇总
- 下一篇: 基于Zabbix IPMI监控服务器硬件