本文參考鏈接:
Jamin
HTTPS信任證書的三種方法
在2016年底, Apple要求所有App適配App Transport Security,簡(jiǎn)單的說就是除了特殊情況外月培,App跟服務(wù)端通信必須使用HTTPS協(xié)議,否則將阻止明文的HTTP請(qǐng)求恩急,從2017年1月1日起杉畜,所有的新提交app默認(rèn)是不允許使用 Allow Arbitrary Loads來繞過ATS限制的。
但是在2017年1月11日衷恭,Apple發(fā)布聲明宣布延長(zhǎng)這個(gè)時(shí)限寻行,提供開發(fā)者更多的時(shí)間來準(zhǔn)備適應(yīng),目前Apple仍未發(fā)布最新的截止日期匾荆。
雖然這個(gè)消息讓很多iOS開發(fā)者長(zhǎng)出一口氣拌蜘,至少目前不必?fù)?dān)心了,但是如果你不準(zhǔn)備好HTTPS牙丽,那么它終究是你頭頂上的達(dá)摩克利斯之劍简卧,作為一名有追求的iOS開發(fā)者,有必要更深入的了解HTTPS烤芦。
引讀: 互聯(lián)網(wǎng)全站HTTPS的時(shí)代已經(jīng)到來
一. HTTPS
其實(shí)HTTPS從最終的數(shù)據(jù)解析的角度举娩,與HTTP沒有任何的區(qū)別,HTTPS就是將HTTP協(xié)議數(shù)據(jù)包放到SSL/TSL層加密后构罗,在TCP/IP層組成IP數(shù)據(jù)報(bào)去傳輸铜涉,以此保證傳輸數(shù)據(jù)的安全;而對(duì)于接收端遂唧,在SSL/TSL將接收的數(shù)據(jù)包解密之后芙代,將數(shù)據(jù)傳給HTTP協(xié)議層,就是普通的HTTP數(shù)據(jù)盖彭。HTTP和SSL/TSL都處于OSI模型的應(yīng)用層纹烹。從HTTP切換到HTTPS是一個(gè)非常簡(jiǎn)單的過程页滚,在做具體的切換操作之前,我們需要了解幾個(gè)概念:
SSL/TSL
關(guān)于SSL/TSL铺呵,阮一峰的兩篇博客文章做了很好的介紹:
簡(jiǎn)單的來說裹驰,SSL/TSL通過四次握手,主要交換三個(gè)信息:
數(shù)字證書:該證書包含了公鑰等信息片挂,一般是由服務(wù)器發(fā)給客戶端幻林,接收方通過驗(yàn)證這個(gè)證書是不是由信賴的CA簽發(fā),或者與本地的證書相對(duì)比音念,來判斷證書是否可信滋将;假如需要雙向驗(yàn)證,則服務(wù)器和客戶端都需要發(fā)送數(shù)字證書給對(duì)方驗(yàn)證症昏。
三個(gè)隨機(jī)數(shù):這三個(gè)隨機(jī)數(shù)構(gòu)成了后續(xù)通信過程中用來對(duì)數(shù)據(jù)進(jìn)行對(duì)稱加密解密的“對(duì)話密鑰”。首先客戶端先發(fā)第一個(gè)隨機(jī)數(shù)N1父丰,然后服務(wù)器回了第二個(gè)隨機(jī)數(shù)N2(這個(gè)過程同時(shí)把之前提到的證書發(fā)給客戶端)肝谭,這兩個(gè)隨機(jī)數(shù)都是明文的;而第三個(gè)隨機(jī)數(shù)N3(這個(gè)隨機(jī)數(shù)被稱為Premaster secret)蛾扇,客戶端用數(shù)字證書的公鑰進(jìn)行非對(duì)稱加密攘烛,發(fā)給服務(wù)器;而服務(wù)器用只有自己知道的私鑰來解密镀首,獲取第三個(gè)隨機(jī)數(shù)坟漱。只有服務(wù)端和客戶端都有了三個(gè)隨機(jī)數(shù)N1+N2+N3,然后兩端就使用這三個(gè)隨機(jī)數(shù)來生成“對(duì)話密鑰”更哄,在此之后的通信都是使用這個(gè)“對(duì)話密鑰”來進(jìn)行對(duì)稱加密解密芋齿。因?yàn)檫@個(gè)過程中,服務(wù)端的私鑰只用來解密第三個(gè)隨機(jī)數(shù)成翩,從來沒有在網(wǎng)絡(luò)中傳輸過觅捆,這樣的話,只要私鑰沒有被泄露麻敌,那么數(shù)據(jù)就是安全的栅炒。
加密通信協(xié)議:就是雙方商量使用哪一種加密方式,假如兩者支持的加密方式不匹配术羔,則無法進(jìn)行通信赢赊。
有個(gè)常見的問題,關(guān)于隨機(jī)數(shù)為什么要三個(gè)级历?只最后一個(gè)隨機(jī)數(shù)N3不可以么释移?
這是由于SSL/TLS設(shè)計(jì),就假設(shè)服務(wù)器不相信所有的客戶端都能夠提供完全隨機(jī)數(shù)寥殖,假如某個(gè)客戶端提供的隨機(jī)數(shù)不隨機(jī)的話秀鞭,就大大增加了“對(duì)話密鑰”被破解的風(fēng)險(xiǎn)趋观,所以由三組隨機(jī)數(shù)組成最后的隨機(jī)數(shù),保證了隨機(jī)數(shù)的隨機(jī)性锋边,以此來保證每次生成的“對(duì)話密鑰”安全性皱坛。
數(shù)字證書
數(shù)字證書是一個(gè)電子文檔,其中包含了持有者的信息豆巨、公鑰以及證明該證書有效的數(shù)字簽名剩辟。而數(shù)字證書以及相關(guān)的公鑰管理和驗(yàn)證等技術(shù)組成了PKI(公鑰基礎(chǔ)設(shè)施)規(guī)范體系。一般來說往扔,數(shù)字證書是由數(shù)字證書認(rèn)證機(jī)構(gòu)(Certificate authority贩猎,即CA)來負(fù)責(zé)簽發(fā)和管理,并承擔(dān)PKI體系中公鑰合法性的檢驗(yàn)責(zé)任萍膛;數(shù)字證書的類型有很多吭服,而HTTPS使用的是SSL證書。
怎么來驗(yàn)證數(shù)字證書是由CA簽發(fā)的蝗罗,而不是第三方偽造的呢艇棕? 在回答這個(gè)問題前,我們需要先了解CA的組織結(jié)構(gòu)串塑。首先沼琉,CA組織結(jié)構(gòu)中,最頂層的就是根CA桩匪,根CA下可以授權(quán)給多個(gè)二級(jí)CA打瘪,而二級(jí)CA又可以授權(quán)多個(gè)三級(jí)CA,所以CA的組織結(jié)構(gòu)是一個(gè)樹結(jié)構(gòu)傻昙。了解了CA的組織結(jié)構(gòu)后闺骚,來看看數(shù)字證書的簽發(fā)流程:
數(shù)字證書的簽發(fā)機(jī)構(gòu)CA,在接收到申請(qǐng)者的資料后進(jìn)行核對(duì)并確定信息的真實(shí)有效妆档,然后就會(huì)制作一份符合X.509標(biāo)準(zhǔn)的文件葛碧。證書中的證書內(nèi)容包括了持有者信息和公鑰等都是由申請(qǐng)者提供的,而數(shù)字簽名則是CA機(jī)構(gòu)對(duì)證書內(nèi)容進(jìn)行hash加密后等到的过吻,而這個(gè)數(shù)字簽名就是我們驗(yàn)證證書是否是有可信CA簽發(fā)的數(shù)據(jù)进泼。
接收端接到一份數(shù)字證書Cer1后,對(duì)證書的內(nèi)容做Hash等到H1纤虽;然后在簽發(fā)該證書的機(jī)構(gòu)CA1的數(shù)字證書中找到公鑰乳绕,對(duì)證書上數(shù)字簽名進(jìn)行解密,得到證書Cer1簽名的Hash摘要H2逼纸;對(duì)比H1和H2洋措,假如相等,則表示證書沒有被篡改杰刽。但這個(gè)時(shí)候還是不知道CA是否是合法的菠发,我們看到上圖中有CA機(jī)構(gòu)的數(shù)字證書王滤,這個(gè)證書是公開的,所有人都可以獲取到滓鸠。而這個(gè)證書中的數(shù)字簽名是上一級(jí)生成的雁乡,所以可以這樣一直遞歸驗(yàn)證下去,直到根CA糜俗。根CA是自驗(yàn)證的踱稍,即他的數(shù)字簽名是由自己的私鑰來生成的。合法的根CA會(huì)被瀏覽器和操作系統(tǒng)加入到權(quán)威信任CA列表中悠抹,這樣就完成了最終的驗(yàn)證珠月。所以,一定要保護(hù)好自己環(huán)境(瀏覽器/操作系統(tǒng))中根CA信任列表楔敌,信任了根CA就表示信任所有根CA下所有子級(jí)CA所簽發(fā)的證書啤挎,不要隨便添加根CA證書。
了解了上面兩個(gè)概念之后卵凑,對(duì)HTTPS就有了個(gè)初步的了解庆聘,下面我們看如何在iOS上實(shí)現(xiàn)對(duì)HTTPS的支持。
二. 實(shí)現(xiàn)APP支持HTTPS
首先氛谜,需要明確你使用HTTP/HTTPS的用途,因?yàn)镺SX和iOS平臺(tái)提供了多種API区端,來支持不同的用途值漫,官方文檔《Making HTTP and HTTPS Requests》有詳細(xì)的說明,而文檔《HTTPS Server Trust Evaluation》則詳細(xì)講解了HTTPS驗(yàn)證相關(guān)知識(shí)织盼,這里就不多說了杨何。本文主要講解我們最常用的NSURLSession支持HTTPS的實(shí)現(xiàn),以及怎么樣使用AFNetworking這個(gè)非常流行的第三方庫(kù)來支持HTTPS沥邻。本文假設(shè)你對(duì)HTTP以及NSURLSession的接口有了足夠的了解危虱。
使用NSURLSession來支持HTTPS
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURL *url = [NSURL URLWithString:@"https://域名"];
// 發(fā)起數(shù)據(jù)任務(wù)
[[self.session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@---%@",response,[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
// 在代理方法中實(shí)現(xiàn)證書的信任
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler {
/*
<NSURLProtectionSpace: 0x7fef2b686e20>:
Host:mail.itcast.cn,
Server:https,
Auth-Scheme:NSURLAuthenticationMethodServerTrust,
*/
// 判斷是否是信任服務(wù)器證書
if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
// 告訴服務(wù)器,客戶端信任證書
// 創(chuàng)建憑據(jù)對(duì)象
NSURLCredential *credntial = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
// 通過completionHandler告訴服務(wù)器信任證書
completionHandler(NSURLSessionAuthChallengeUseCredential,credntial);
}
NSLog(@"protectionSpace = %@",challenge.protectionSpace);
}
上面是代碼是通過系統(tǒng)默認(rèn)驗(yàn)證流程來驗(yàn)證證書的唐全。假如我們是自建證書的呢埃跷?這樣的話服務(wù)器的證書因?yàn)椴皇强尚湃蔚腃A簽發(fā)的,所以直接使用進(jìn)行驗(yàn)證是不會(huì)成功的邮利。又或者弥雹,即使服務(wù)器返回的證書是信任CA簽發(fā)的,又如何確定這證書就是我們想要的特定證書延届?這就需要先在本地導(dǎo)入證書剪勿,再驗(yàn)證。
使用AFNetworking來支持HTTPS
AFNetworking是iOS/OSX開發(fā)最流行的第三方開源庫(kù)之一方庭,其作者是非常著名的iOS/OSX開發(fā)者Mattt Thompson厕吉,其博客NSHipster也是iOS/OSX開發(fā)者學(xué)習(xí)和開闊技術(shù)視野的好地方酱固。AFNetworking已經(jīng)將上面的邏輯代碼封裝好,甚至更完善头朱,在AFSecurityPolicy文件中运悲,有興趣可以閱讀這個(gè)模塊的代碼。 下面是代碼部分:
// 1.初始化單例類
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
mgr.securityPolicy.SSLPinningMode = AFSSLPinningModeCertificate;
// 2.設(shè)置證書模式
NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"xxx" ofType:@"此處填文件的拓展名"];
NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
mgr.securityPolicy.pinnedCertificates = [[NSArray alloc] initWithObjects:cerData, nil];
// 客戶端是否信任非法證書
mgr.securityPolicy.allowInvalidCertificates = YES;
// 是否在證書域字段中驗(yàn)證域名
[mgr.securityPolicy setValidatesDomainName:NO];
最后建議采用本地導(dǎo)入證書的方式驗(yàn)證證書髓窜,來保證足夠的安全性扇苞。
更多的驗(yàn)證方法,請(qǐng)查看官方文檔《HTTPS Server Trust Evaluation》