iOS开发HTTPS实现之信任SSL证书和自签名证书
首先來(lái)分析一下什么是HTTPS以及了解HTTPS對(duì)于iOS開(kāi)發(fā)者的意義
HTTPS 以及SSL/TSL
什么是SSL?
SSL(Secure Sockets Layer, 安全套接字層),因?yàn)樵然ヂ?lián)網(wǎng)上使用的 HTTP 協(xié)議是明文的,存在很多缺點(diǎn),比如傳輸內(nèi)容會(huì)被偷窺(嗅探)和篡改。 SSL 協(xié)議的作用就是在傳輸層對(duì)網(wǎng)絡(luò)連接進(jìn)行加密。
何為TLS?
到了1999年,SSL 因?yàn)閼?yīng)用廣泛,已經(jīng)成為互聯(lián)網(wǎng)上的事實(shí)標(biāo)準(zhǔn)。IETF 就在那年把 SSL 標(biāo)準(zhǔn)化。標(biāo)準(zhǔn)化之后的名稱改為 TLS(Transport Layer Security,傳輸層安全協(xié)議)。SSL與TLS可以視作同一個(gè)東西的不同階段
HTTPS
簡(jiǎn)單來(lái)說(shuō),HTTPS = HTTP + SSL/TLS, 也就是 HTTP over SSL 或 HTTP over TLS,這是后面加 S 的由來(lái) 。
HTTPS和HTTP異同:HTTP和HTTPS使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。HTTP的連接很簡(jiǎn)單,是無(wú)狀態(tài)的;HTTPS協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議,比HTTP協(xié)議安全。
在WWDC 2016開(kāi)發(fā)者大會(huì)上,蘋果宣布了一個(gè)最后期限:到2017年1月1日 App Store中的所有應(yīng)用都必須啟用 App Transport Security安全功能。App Transport Security(ATS)是蘋果在iOS 9中引入的一項(xiàng)隱私保護(hù)功能,屏蔽明文HTTP資源加載,連接必須經(jīng)過(guò)更安全的HTTPS。蘋果目前允許開(kāi)發(fā)者暫時(shí)關(guān)閉ATS,可以繼續(xù)使用HTTP連接,但到年底所有官方商店的應(yīng)用都必須強(qiáng)制性使用ATS。
所以對(duì)于iOS開(kāi)發(fā)者來(lái)說(shuō),需要盡早解決HTTPS請(qǐng)求的問(wèn)題。
發(fā)送HTTPS請(qǐng)求信任SSL證書和自簽名證書,分為三種情況
1.如果你的app服務(wù)端安裝的是SLL頒發(fā)的CA,可以使用系統(tǒng)方法直接實(shí)現(xiàn)信任SSL證書,關(guān)于Apple對(duì)SSL證書的要求請(qǐng)參考:蘋果官方文檔CertKeyTrustProgGuide
這種方式不需要在Bundle中引入CA文件,可以交給系統(tǒng)去判斷服務(wù)器端的證書是不是SSL證書,驗(yàn)證過(guò)程也不需要我們?nèi)ゾ唧w實(shí)現(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)然,如果你需要同時(shí)信任SSL證書和自簽名證書的話還是需要在代碼中實(shí)現(xiàn)CA的驗(yàn)證,這種情況在后面會(huì)提到。
2.基于AFNetWorking的SSL特定服務(wù)器證書信任處理,重寫AFNetWorking的customSecurityPolicy方法,這里我創(chuàng)建了一個(gè)HttpRequest類,分別對(duì)GET和POST方法進(jìn)行了封裝,以GET方法為例:
+?(void)get:(NSString?*)url?params:(NSDictionary?*)params?success:(void?(^)(id))success?failure:(void?(^)(NSError?*))failure?{????//?1.獲得請(qǐng)求管理者AFHTTPRequestOperationManager?*mgr?=?[AFHTTPRequestOperationManager?manager];????//?2.申明返回的結(jié)果是text/html類型mgr.responseSerializer?=?[AFHTTPResponseSerializer?serializer];????//?3.設(shè)置超時(shí)時(shí)間為10smgr.requestSerializer.timeoutInterval?=?10;????//?加上這行代碼,https?ssl?驗(yàn)證。if(openHttpsSSL)?{[mgr?setSecurityPolicy:[self?customSecurityPolicy]];}????//?4.發(fā)送GET請(qǐng)求[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?使用證書驗(yàn)證模式AFSecurityPolicy?*securityPolicy?=?[AFSecurityPolicy?policyWithPinningMode:AFSSLPinningModeCertificate];????//?allowInvalidCertificates?是否允許無(wú)效證書(也就是自建的證書),默認(rèn)為NO//?如果是需要驗(yàn)證自建證書,需要設(shè)置為YESsecurityPolicy.allowInvalidCertificates?=?YES;????//validatesDomainName?是否需要驗(yàn)證域名,默認(rèn)為YES;//假如證書的域名與你請(qǐng)求的域名不一致,需把該項(xiàng)設(shè)置為NO;如設(shè)成NO的話,即服務(wù)器使用其他可信任機(jī)構(gòu)頒發(fā)的證書,也可以建立連接,這個(gè)非常危險(xiǎn),建議打開(kāi)。//置為NO,主要用于這種情況:客戶端請(qǐng)求的是子域名,而證書上的是另外一個(gè)域名。因?yàn)镾SL證書上的域名是獨(dú)立的,假如證書上注冊(cè)的域名是www.google.com,那么mail.google.com是無(wú)法驗(yàn)證通過(guò)的;當(dāng)然,有錢可以注冊(cè)通配符的域名*.google.com,但這個(gè)還是比較貴的。//如置為NO,建議自己添加對(duì)應(yīng)域名的校驗(yàn)邏輯。securityPolicy.validatesDomainName?=?NO;securityPolicy.pinnedCertificates?=?@[certData];????return?securityPolicy; }其中的cerPath就是app bundle中證書路徑,certificate為證書名稱的宏,僅支持cer格式,securityPolicy的相關(guān)配置尤為重要,請(qǐng)仔細(xì)閱讀customSecurityPolicy方法并根據(jù)實(shí)際情況設(shè)置其屬性。
這樣,就能夠在AFNetWorking的基礎(chǔ)上使用HTTPS協(xié)議訪問(wèn)特定服務(wù)器,但是不能信任根證書的CA文件,因此這種方式存在風(fēng)險(xiǎn),讀取pinnedCertificates中的證書數(shù)組的時(shí)候有可能失敗,如果證書不符合,certData就會(huì)為nil。
3.更改系統(tǒng)方法,發(fā)送異步NSURLConnection請(qǐng)求。
-?(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]; }重點(diǎn)在于處理NSURLConnection的didReceiveAuthenticationChallenge代理方法,對(duì)CA文件進(jìn)行驗(yàn)證,并建立信任連接。
-?(BOOL)connection:(NSURLConnection?*)connection?canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace?*)protectionSpace?{????return?[protectionSpace.authenticationMethod?isEqualToString:NSURLAuthenticationMethodServerTrust]; }-?(void)connection:(NSURLConnection?*)connection?didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge?*)challenge?{?/*//直接驗(yàn)證服務(wù)器是否被認(rèn)證(serverTrust),這種方式直接忽略證書驗(yàn)證,直接建立連接,但不能過(guò)濾其它URL連接,可以理解為一種折衷的處理方式,實(shí)際上并不安全,因此不推薦。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ù)官方文檔的說(shuō)明,判斷(result == kSecTrustResultUnspecified) || (result == kSecTrustResultProceed)的值,若為1,則該網(wǎng)站的CA被app信任成功,可以建立數(shù)據(jù)連接,這意味著所有由該CA簽發(fā)的各個(gè)服務(wù)器證書都被信任,而訪問(wèn)其它沒(méi)有被信任的任何網(wǎng)站都會(huì)連接失敗。該CA文件既可以是SLL也可以是自簽名。
NSURLConnection的其它代理方法實(shí)現(xiàn)
#pragma?mark?--?connect的異步代理方法-(void)connection:(NSURLConnection?*)connection?didReceiveResponse:(NSURLResponse?*)response?{????NSLog(@"請(qǐng)求被響應(yīng)");_mData?=?[[NSMutableData?alloc]init]; }-(void)connection:(NSURLConnection?*)connection?didReceiveData:(nonnull?NSData?*)data?{????NSLog(@"開(kāi)始返回?cái)?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); }//鏈接出錯(cuò)-(void)connection:(NSURLConnection?*)connection?didFailWithError:(NSError?*)error?{????NSLog(@"error?-?%@",error); }至此,HTTPS信任證書的問(wèn)題得以解決,這不僅是為了響應(yīng)Apple強(qiáng)制性使用ATS的要求,也是為了實(shí)際生產(chǎn)環(huán)境安全性的考慮,HTTPS是未來(lái)的趨勢(shì),建議盡早支持。
如需參考Demo請(qǐng)移步本人在Github上的開(kāi)源項(xiàng)目
文/無(wú)忌不悔(簡(jiǎn)書作者)
原文鏈接:http://www.jianshu.com/p/6b9c8bd5005a#
著作權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),并標(biāo)注“簡(jiǎn)書作者”。
轉(zhuǎn)載于:https://blog.51cto.com/ljianbing/1874196
總結(jié)
以上是生活随笔為你收集整理的iOS开发HTTPS实现之信任SSL证书和自签名证书的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: [转]优秀Python学习资源收集汇总
- 下一篇: 基于Zabbix IPMI监控服务器硬件