- HTTPS
其實HTTPS從最終的數(shù)據(jù)解析的角度,與HTTP沒有任何的區(qū)別毙籽,HTTPS就是將HTTP協(xié)議數(shù)據(jù)包放到SSL/TSL層加密后役首,在TCP/IP層組成IP數(shù)據(jù)報去傳輸,以此保證傳輸數(shù)據(jù)的安全掺逼;而對于接收端吃媒,在SSL/TSL將接收的數(shù)據(jù)包解密之后,將數(shù)據(jù)傳給HTTP協(xié)議層吕喘,就是普通的HTTP數(shù)據(jù)赘那。HTTP和SSL/TSL都處于OSI模型的應(yīng)用層。從HTTP切換到HTTPS是一個非常簡單的過程氯质,在做具體的切換操作之前募舟,我們需要了解幾個概念:
SSL/TSL
關(guān)于SSL/TSL,阮一峰的兩篇博客文章做了很好的介紹:
SSL/TLS協(xié)議運行機制的概述
圖解SSL/TLS協(xié)議
簡單的來說闻察,SSL/TSL通過四次握手拱礁,主要交換三個信息:
數(shù)字證書:該證書包含了公鑰等信息,一般是由服務(wù)器發(fā)給客戶端辕漂,接收方通過驗證這個證書是不是由信賴的CA簽發(fā)呢灶,或者與本地的證書相對比,來判斷證書是否可信钉嘹;假如需要雙向驗證鸯乃,則服務(wù)器和客戶端都需要發(fā)送數(shù)字證書給對方驗證;
三個隨機數(shù):
這三個隨機數(shù)構(gòu)成了后續(xù)通信過程中用來對數(shù)據(jù)進(jìn)行對稱加密解密的“對話密鑰”跋涣。
首先客戶端先發(fā)第一個隨機數(shù)N1缨睡,然后服務(wù)器回了第二個隨機數(shù)N2(這個過程同時把之前提到的證書發(fā)給客戶端),這兩個隨機數(shù)都是明文的陈辱;而第三個隨機數(shù)N3(這個隨機數(shù)被稱為Premaster secret)奖年,客戶端用數(shù)字證書的公鑰進(jìn)行非對稱加密,發(fā)給服務(wù)器性置;而服務(wù)器用只有自己知道的私鑰來解密拾并,獲取第三個隨機數(shù)。這樣鹏浅,服務(wù)端和客戶端都有了三個隨機數(shù)N1+N2+N3嗅义,然后兩端就使用這三個隨機數(shù)來生成“對話密鑰”,在此之后的通信都是使用這個“對話密鑰”來進(jìn)行對稱加密解密隐砸。因為這個過程中之碗,服務(wù)端的私鑰只用來解密第三個隨機數(shù),從來沒有在網(wǎng)絡(luò)中傳輸過季希,這樣的話褪那,只要私鑰沒有被泄露幽纷,那么數(shù)據(jù)就是安全的。
加密通信協(xié)議:就是雙方商量使用哪一種加密方式博敬,假如兩者支持的加密方式不匹配友浸,則無法進(jìn)行通信;
有個常見的問題偏窝,關(guān)于隨機數(shù)為什么要三個收恢?只最后一個隨機數(shù)N3不可以么?
這是由于SSL/TLS設(shè)計祭往,就假設(shè)服務(wù)器不相信所有的客戶端都能夠提供完全隨機數(shù)伦意,假如某個客戶端提供的隨機數(shù)不隨機的話,就大大增加了“對話密鑰”被破解的風(fēng)險硼补,所以由三組隨機數(shù)組成最后的隨機數(shù)驮肉,保證了隨機數(shù)的隨機性,以此來保證每次生成的“對話密鑰”安全性已骇。
數(shù)字證書
數(shù)字證書是一個電子文檔离钝,其中包含了持有者的信息、公鑰以及證明該證書有效的數(shù)字簽名褪储。而數(shù)字證書以及相關(guān)的公鑰管理和驗證等技術(shù)組成了PKI(公鑰基礎(chǔ)設(shè)施)規(guī)范體系奈辰。一般來說,數(shù)字證書是由數(shù)字證書認(rèn)證機構(gòu)(Certificate authority乱豆,即CA)來負(fù)責(zé)簽發(fā)和管理,并承擔(dān)PKI體系中公鑰合法性的檢驗責(zé)任吊趾;數(shù)字證書的類型有很多宛裕,而HTTPS使用的是SSL證書。
怎么來驗證數(shù)字證書是由CA簽發(fā)的论泛,而不是第三方偽造的呢揩尸? 在回答這個問題前,我們需要先了解CA的組織結(jié)構(gòu)屁奏。首先岩榆,CA組織結(jié)構(gòu)中,最頂層的就是根CA坟瓢,根CA下可以授權(quán)給多個二級CA勇边,而二級CA又可以授權(quán)多個三級CA,所以CA的組織結(jié)構(gòu)是一個樹結(jié)構(gòu)折联。對于SSL證書市場來說粒褒,主要被Symantec(旗下有VeriSign和GeoTrust)、Comodo SSL诚镰、Go Daddy 和 GlobalSign 瓜分奕坟。 了解了CA的組織結(jié)構(gòu)后祥款,來看看數(shù)字證書的簽發(fā)流程:
數(shù)字證書的簽發(fā)流程
數(shù)字證書的簽發(fā)機構(gòu)CA,在接收到申請者的資料后進(jìn)行核對并確定信息的真實有效月杉,然后就會制作一份符合X.509標(biāo)準(zhǔn)的文件刃跛。證書中的證書內(nèi)容包含的持有者信息和公鑰等都是由申請者提供的,而數(shù)字簽名則是CA機構(gòu)對證書內(nèi)容進(jìn)行hash加密后得到的苛萎,而這個數(shù)字簽名就是我們驗證證書是否是有可信CA簽發(fā)的數(shù)據(jù)桨昙。
數(shù)字證書的驗證流程
假設(shè)上圖證書是由證書簽發(fā)機構(gòu)CA1簽發(fā)的。
1)接收端接到一份數(shù)字證書Cer1后首懈,對證書的內(nèi)容做Hash得到H1绊率;
2)從簽發(fā)該證書的機構(gòu)CA1的數(shù)字證書中找到公鑰,對證書上數(shù)字簽名進(jìn)行解密究履,得到證書Cer1簽名的Hash摘要H2滤否;
3)對比H1和H2,如相等最仑,則表示證書沒有被篡改藐俺。
4)但這個時候還是不知道CA是否是合法的,我們看到上圖中有CA機構(gòu)的數(shù)字證書泥彤,這個證書是公開的欲芹,所有人都可以獲取到。而這個證書中的數(shù)字簽名是上一級生成的吟吝,所以可以這樣一直遞歸驗證下去菱父,直到根CA。根CA是自驗證的剑逃,即他的數(shù)字簽名是由自己的私鑰來生成的浙宜。合法的根CA會被瀏覽器和操作系統(tǒng)加入到權(quán)威信任CA列表中,這樣就完成了最終的驗證蛹磺。所以粟瞬,一定要保護(hù)好自己環(huán)境(瀏覽器/操作系統(tǒng))中根CA信任列表,信任了根CA就表示信任所有根CA下所有子級CA所簽發(fā)的證書萤捆,不要隨便添加根CA證書裙品。
一般操作系統(tǒng)和瀏覽器只包含根CA機構(gòu)的證書,而在配置Web服務(wù)器的HTTPS時俗或,也會將配置整個證書鏈市怎,所以整個校驗流程是從最后的葉子節(jié)點證書開始,用父節(jié)點校驗子節(jié)點蕴侣,一層層校驗整個證書鏈的可信性焰轻。
打個比喻:父(根CA數(shù)字證書)-子(CA數(shù)字證書)-孫(數(shù)字證書)三代人,假設(shè)父沒有其他兄弟(相當(dāng)于根CA機構(gòu)是唯一的)昆雀,假如子與父進(jìn)行DNA親子鑒定辱志,檢測DNA位點(即證書簽名)相同蝠筑,那就基本確定子是由父所生;孫與子一樣揩懒。這樣就能夠確定孫肯定是源于父一脈什乙,是父(根CA數(shù)字證書)的合法繼承人。數(shù)字證書的驗證就是基于同樣的原理已球。
Basic Constraint校驗漏洞
那是否不管多少層都可以這樣一直信任下去呢臣镣?理論上是可行的,但會遇到一個問題智亮。假設(shè)我從可信CA機構(gòu)購買了一張證書忆某,使用這張證書簽發(fā)的證書是否也會被操作系統(tǒng)和瀏覽器信任呢?明顯是不應(yīng)該相信的阔蛉,因為我并不是CA機構(gòu)弃舒,假如我簽發(fā)的證書也被信任的話,那我完全可以自己簽發(fā)任何域名的證書來進(jìn)行偽造攻擊状原。這就是著名的Basic Constraint校驗漏洞聋呢,X.509證書中的Basic Constraint包含了這是不是一個CA機構(gòu),以及有效證書路徑的最大深度(即颠区,這個CA還能否繼續(xù)簽發(fā)CA機構(gòu)證書及其簽發(fā)子CA證書的路徑深度)削锰。但在幾年前,包括微軟和Apple都爆出了沒有正確校驗這些信息的漏洞毕莱。
了解了上面關(guān)于SSL/TSL通信加密策略以及數(shù)字證書的概念之后器贩,對HTTPS的安全機制就有了個初步的了解,下面我們看如何在iOS上實現(xiàn)對HTTPS的支持朋截。
- 實現(xiàn)支持HTTPS
首先磨澡,需要明確你使用HTTP/HTTPS的用途,因為OSX和iOS平臺提供了多種API质和,來支持不同的用途,官方文檔《Making HTTP and HTTPS Requests》有詳細(xì)的說明稚字,而文檔《HTTPS Server Trust Evaluation》則詳細(xì)講解了HTTPS驗證相關(guān)知識饲宿,這里就不多說了。本文主要講解我們最常用的NSURLConnection支持HTTPS的實現(xiàn)(NSURLSession的實現(xiàn)方法類似胆描,只是要求授權(quán)證明的回調(diào)不一樣而已)瘫想,以及怎么樣使用AFNetworking這個非常流行的第三方庫來支持HTTPS。本文假設(shè)你對HTTP以及NSURLConnection的接口有了足夠的了解昌讲。驗證證書的相關(guān)Api在Security Framework中国夜,驗證流程如下:
1). 第一步,先獲取需要驗證的信任對象(Trust Object)短绸。這個Trust Object在不同的應(yīng)用場景下獲取的方式都不一樣车吹,對于NSURLConnection來說筹裕,是從delegate方法-connection:willSendRequestForAuthenticationChallenge:回調(diào)回來的參數(shù)challenge中獲取([challenge.protectionSpace serverTrust])。
2). 使用系統(tǒng)默認(rèn)驗證方式驗證Trust Object窄驹。SecTrustEvaluate會根據(jù)Trust Object的驗證策略朝卒,一級一級往上,驗證證書鏈上每一級數(shù)字簽名的有效性(上一部分有講解)乐埠,從而評估證書的有效性抗斤。
3). 如第二步驗證通過了,一般的安全要求下丈咐,就可以直接驗證通過瑞眼,進(jìn)入到下一步:使用Trust Object生成一份憑證([NSURLCredential credentialForTrust:serverTrust]),傳入challenge的sender中([challenge.sender useCredential:cred forAuthenticationChallenge:challenge])處理棵逊,建立連接伤疙。
4). 假如有更強的安全要求,可以繼續(xù)對Trust Object進(jìn)行更嚴(yán)格的驗證歹河。常用的方式是在本地導(dǎo)入證書掩浙,驗證Trust Object與導(dǎo)入的證書是否匹配。更多的方法可以查看Enforcing Stricter Server Trust Evaluation秸歧,這一部分在講解AFNetworking源碼中會講解到厨姚。
5). 假如驗證失敗,取消此次Challenge-Response Authentication驗證流程键菱,拒絕連接請求谬墙。
ps: 假如是自建證書的,則不使用第二步系統(tǒng)默認(rèn)的驗證方式经备,因為自建證書的根CA的數(shù)字簽名未在操作系統(tǒng)的信任列表中拭抬。
iOS授權(quán)驗證的API和流程大概了解了,下面侵蒙,我們看看在NSURLConnection中的代碼實現(xiàn):
// Now start the connection
NSURL * httpsURL = [NSURL URLWithString:@"https://www.google.com"];
self.connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:httpsURL] delegate:self];
//回調(diào)
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
//1)獲取trust object
SecTrustRef trust = challenge.protectionSpace.serverTrust;
SecTrustResultType result;
//2)SecTrustEvaluate對trust進(jìn)行驗證
OSStatus status = SecTrustEvaluate(trust, &result);
if (status == errSecSuccess &&
(result == kSecTrustResultProceed ||
result == kSecTrustResultUnspecified)) {
//3)驗證成功造虎,生成NSURLCredential憑證cred,告知challenge的sender使用這個憑證來繼續(xù)連接
NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
[challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
} else {
//5)驗證失敗纷闺,取消這次驗證流程
[challenge.sender cancelAuthenticationChallenge:challenge];
}
}
上面是代碼是通過系統(tǒng)默認(rèn)驗證流程來驗證證書的算凿。假如我們是自建證書的呢?這樣Trust Object里面服務(wù)器的證書因為不是可信任的CA簽發(fā)的犁功,所以直接使用SecTrustEvaluate進(jìn)行驗證是不會成功氓轰。又或者,即使服務(wù)器返回的證書是信任CA簽發(fā)的浸卦,又如何確定這證書就是我們想要的特定證書署鸡?這就需要先在本地導(dǎo)入證書,設(shè)置成需要參與驗證的Anchor Certificate(錨點證書,通過SecTrustSetAnchorCertificates設(shè)置了參與校驗錨點證書之后靴庆,假如驗證的數(shù)字證書是這個錨點證書的子節(jié)點时捌,即驗證的數(shù)字證書是由錨點證書對應(yīng)CA或子CA簽發(fā)的,或是該證書本身撒穷,則信任該證書)匣椰,再調(diào)用SecTrustEvaluate來驗證。代碼如下:
//先導(dǎo)入證書
NSString * cerPath = ...; //證書的路徑
NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
self.trustedCertificates = @[CFBridgingRelease(certificate)];
//回調(diào)
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
//1)獲取trust object
SecTrustRef trust = challenge.protectionSpace.serverTrust;
SecTrustResultType result;
//注意:這里將之前導(dǎo)入的證書設(shè)置成下面驗證的Trust Object的anchor certificate
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)self.trustedCertificates);
//2)SecTrustEvaluate會查找前面SecTrustSetAnchorCertificates設(shè)置的證書或者系統(tǒng)默認(rèn)提供的證書端礼,對trust進(jìn)行驗證
OSStatus status = SecTrustEvaluate(trust, &result);
if (status == errSecSuccess &&
(result == kSecTrustResultProceed ||
result == kSecTrustResultUnspecified)) {
//3)驗證成功禽笑,生成NSURLCredential憑證cred,告知challenge的sender使用這個憑證來繼續(xù)連接
NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
[challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
} else {
//5)驗證失敗蛤奥,取消這次驗證流程
[challenge.sender cancelAuthenticationChallenge:challenge];
}
}
建議采用本地導(dǎo)入證書的方式驗證證書佳镜,來保證足夠的安全性。更多的驗證方法凡桥,請查看官方文檔《HTTPS Server Trust Evaluation》
- 使用AFNetworking來支持HTTPS
AFNetworking是iOS/OSX開發(fā)最流行的第三方開源庫之一蟀伸,其作者是非常著名的iOS/OSX開發(fā)者M(jìn)attt Thompson,其博客NSHipster也是iOS/OSX開發(fā)者學(xué)習(xí)和開闊技術(shù)視野的好地方缅刽。AFNetworking已經(jīng)將上面的邏輯代碼封裝好啊掏,甚至更完善,在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 是否允許無效證書(也就是自建的證書),默認(rèn)為NO
//如果是需要驗證自建證書啡省,需要設(shè)置為YES
securityPolicy.allowInvalidCertificates = YES;
//validatesDomainName 是否需要驗證域名娜睛,默認(rèn)為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 = YES;
//validatesCertificateChain 是否驗證整個證書鏈预厌,默認(rèn)為YES
//設(shè)置為YES阿迈,會將服務(wù)器返回的Trust Object上的證書鏈與本地導(dǎo)入的證書進(jìn)行對比,這就意味著轧叽,假如你的證書鏈?zhǔn)沁@樣的:
//GeoTrust Global CA
// Google Internet Authority G2
// *.google.com
//那么苗沧,除了導(dǎo)入*.google.com之外,還需要導(dǎo)入證書鏈上所有的CA證書(GeoTrust Global CA, Google Internet Authority G2)炭晒;
//如是自建證書的時候待逞,可以設(shè)置為YES,增強安全性网严;假如是信任的CA所簽發(fā)的證書识樱,則建議關(guān)閉該驗證,因為整個證書鏈一一比對是完全沒有必要(請查看源代碼)震束;
securityPolicy.validatesCertificateChain = NO;
requestOperationManager.securityPolicy = securityPolicy;
這就是AFNetworking的支持HTTPS的主要配置說明怜庸,AFHTTPSessionManager與之基本一致,就不重復(fù)了垢村。
- 總結(jié)
雖然HTTPS相比于HTTP來說割疾,會有一定的性能上的劣勢,但對于網(wǎng)絡(luò)飛速發(fā)展肝断,移動設(shè)備的性能成倍增長的今天杈曲,安全才是我們更應(yīng)該去考慮的。全網(wǎng)HTTPS并不是那么遙遠(yuǎn)胸懈。
下一篇準(zhǔn)備講內(nèi)存數(shù)據(jù)安全和持久化數(shù)據(jù)的安全担扑,敬請期待。
轉(zhuǎn)載自Jaminzzhang的blog:http://oncenote.com/2014/10/21/Security-1-HTTPS/