HTTPS就是將HTTP協(xié)議數(shù)據(jù)包放到SSL/TSL層加密后,在TCP/IP層組成IP數(shù)據(jù)報(bào)去傳輸咆课,以此保證傳輸數(shù)據(jù)的安全末誓。
證書分為兩種扯俱,一種是花錢向CA(證書認(rèn)證的機(jī)構(gòu))購買的證書,服務(wù)端如果使用的是這類證書的話喇澡,那一般客戶端不需要做什么迅栅,用HTTPS進(jìn)行請(qǐng)求就行了,蘋果內(nèi)置了那些受信任的根證書的晴玖。另一種是自己制作的證書读存,使用這類證書的話是不受信任的(當(dāng)然也不用花錢買),因此需要我們?cè)诖a中將該證書設(shè)置為信任證書呕屎。
對(duì)于一般的小型網(wǎng)站尤其是博客让簿,可以使用自簽名證書來構(gòu)建安全網(wǎng)絡(luò),所謂自簽名證書秀睛,就是自己扮演 CA 機(jī)構(gòu)尔当,自己給自己的服務(wù)器頒發(fā)證書。
SSL協(xié)議的握手過程:(參考)
SSL/TSl握手階段的詳細(xì)過程
- 首先蹂安,客戶端(通常是瀏覽器)先向服務(wù)器發(fā)出加密通信的請(qǐng)求椭迎,這被叫做ClientHello請(qǐng)求。
在這一步藤抡,客戶端主要向服務(wù)器提供以下信息侠碧。
(1) 支持的協(xié)議版本,比如TLS 1.0版缠黍。
(2) 一個(gè)客戶端生成的隨機(jī)數(shù)弄兜,稍后用于生成"對(duì)話密鑰"。
(3) 支持的加密方法瓷式,比如RSA公鑰加密替饿。
(4) 支持的壓縮方法。 - 服務(wù)器收到客戶端請(qǐng)求后贸典,向客戶端發(fā)出回應(yīng)视卢,這叫做SeverHello。服務(wù)器的回應(yīng)包含以下內(nèi)容廊驼。
(1) 確認(rèn)使用的加密通信協(xié)議版本据过,比如TLS 1.0版本。如果瀏覽器與服務(wù)器支持的版本不一致妒挎,服務(wù)器關(guān)閉加密通信绳锅。
(2) 一個(gè)服務(wù)器生成的隨機(jī)數(shù),稍后用于生成"對(duì)話密鑰"酝掩。
(3) 確認(rèn)使用的加密方法鳞芙,比如RSA公鑰加密。
(4) 服務(wù)器證書。 - 客戶端收到服務(wù)器回應(yīng)以后原朝,首先驗(yàn)證服務(wù)器證書驯嘱。如果證書不是可信機(jī)構(gòu)頒布、或者證書中的域名與實(shí)際域名不一致喳坠、或者證書已經(jīng)過期鞠评,就會(huì)向訪問者顯示一個(gè)警告,由其選擇是否還要繼續(xù)通信丙笋。
如果證書沒有問題谢澈,客戶端就會(huì)從證書中取出服務(wù)器的公鑰。然后御板,向服務(wù)器發(fā)送下面三項(xiàng)信息:
(1) 一個(gè)隨機(jī)數(shù)锥忿。該隨機(jī)數(shù)用服務(wù)器公鑰加密,防止被竊聽怠肋。
(2) 編碼改變通知敬鬓,表示隨后的信息都將用雙方商定的加密方法和密鑰發(fā)送。
(3) 客戶端握手結(jié)束通知笙各,表示客戶端的握手階段已經(jīng)結(jié)束钉答。這一項(xiàng)同時(shí)也是前面發(fā)送的所有內(nèi)容的hash值,用來供服務(wù)器校驗(yàn)杈抢。
上面第一項(xiàng)的隨機(jī)數(shù)数尿,是整個(gè)握手階段出現(xiàn)的第三個(gè)隨機(jī)數(shù),又稱"pre-master key"惶楼。有了它以后右蹦,客戶端和服務(wù)器就同時(shí)有了三個(gè)隨機(jī)數(shù),接著雙方就用事先商定的加密方法歼捐,各自生成本次會(huì)話所用的同一把"會(huì)話密鑰"何陆。 - 服務(wù)器收到客戶端的第三個(gè)隨機(jī)數(shù)pre-master key之后,計(jì)算生成本次會(huì)話所用的"會(huì)話密鑰"豹储。然后贷盲,向客戶端最后發(fā)送下面信息。
(1)編碼改變通知剥扣,表示隨后的信息都將用雙方商定的加密方法和密鑰發(fā)送巩剖。
(2)服務(wù)器握手結(jié)束通知,表示服務(wù)器的握手階段已經(jīng)結(jié)束钠怯。這一項(xiàng)同時(shí)也是前面發(fā)送的所有內(nèi)容的hash值球及,用來供客戶端校驗(yàn)。
至此呻疹,整個(gè)握手階段全部結(jié)束。接下來,客戶端與服務(wù)器進(jìn)入加密通信刽锤,就完全是使用普通的HTTP協(xié)議镊尺,只不過用"會(huì)話密鑰"加密內(nèi)容。
證書
證書的申請(qǐng)過程
- 證書申請(qǐng)者向頒發(fā)證書的可信第三方CA提交申請(qǐng)證書相關(guān)信息并思,包括:申請(qǐng)者域名庐氮、申請(qǐng)者生成的公鑰(私鑰自己保存)及證書請(qǐng)求文件.cer等
- CA通過線上、線下等多種手段驗(yàn)證證書申請(qǐng)者提供的信息合法和真實(shí)性宋彼。
- 當(dāng)證書申請(qǐng)者提供的信息審核通過后弄砍,CA向證書申請(qǐng)者頒發(fā)證書,證書內(nèi)容包括明文信息和簽名信息输涕。其中明文信息包括證書頒發(fā)機(jī)構(gòu)音婶、證書有效期、域名莱坎、申請(qǐng)者相關(guān)信息及申請(qǐng)者公鑰等衣式,簽名信息是使用CA私鑰進(jìn)行加密的明文信息。當(dāng)證書申請(qǐng)者獲取到證書后檐什,可以通過安裝的CA證書中的公鑰對(duì)簽名信息進(jìn)行解密并與明文信息進(jìn)行對(duì)比來驗(yàn)證簽名的完整性碴卧。
證書的驗(yàn)證過程
TSL/SSL的握手過程里client會(huì)拿到server返回的證書并且驗(yàn)證證書本身的合法性(驗(yàn)證簽名完整性,驗(yàn)證證書有效期等)
- 驗(yàn)證證書頒發(fā)者的合法性(查找頒發(fā)者的證書并檢查其合法性乃正,這個(gè)過程是遞歸的)
- 證書驗(yàn)證的遞歸過程最終會(huì)成功終止住册,而成功終止的條件是:證書驗(yàn)證過程中遇到了錨點(diǎn)證書,錨點(diǎn)證書通常指:嵌入到操作系統(tǒng)中的根證書(權(quán)威證書頒發(fā)機(jī)構(gòu)頒發(fā)的自簽名證書)瓮具。
證書驗(yàn)證失敗的原因
- 無法找到證書的頒發(fā)者
- 證書過期
- 驗(yàn)證過程中遇到了自簽名證書荧飞,但該證書不是錨點(diǎn)證書。
- 無法找到錨點(diǎn)證書(即在證書鏈的頂端沒有找到合法的根證書)
- 訪問的server的dns地址和證書中的地址不同
iOS實(shí)現(xiàn)支持HTTPS
在OC中當(dāng)使用NSURLConnection
或NSURLSession
建立URL并向服務(wù)器發(fā)送https請(qǐng)求獲取資源時(shí)搭综,服務(wù)器會(huì)使用HTTP狀態(tài)碼401進(jìn)行響應(yīng)(即訪問拒絕)垢箕。此時(shí)NSURLConnection或NSURLSession會(huì)接收到服務(wù)器需要授權(quán)的響應(yīng),當(dāng)客戶端授權(quán)通過后兑巾,才能繼續(xù)從服務(wù)器獲取數(shù)據(jù)条获。如下圖所示:
非自簽名證書驗(yàn)證實(shí)現(xiàn)
當(dāng)客戶端發(fā)送https請(qǐng)求后,服務(wù)器會(huì)返回需要授權(quán)的相關(guān)信息蒋歌,對(duì)于NSURLSession而言,帅掘,需要代理對(duì)象實(shí)現(xiàn)URLSession:task:didReceiveChallenge:completionHandler:
方法。
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
NSURLCredential *credential = nil;
SecTrustRef trust = challenge.protectionSpace.serverTrust;
SecTrustResultType result;
OSStatus status = SecTrustEvaluate(trust, &result);
if (status == kSecTrustResultUnspecified || status == kSecTrustResultProceed) {
credential = [NSURLCredential credentialForTrust:trust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
}
}
completionHandler(disposition, credential);
}
-
NSURLSessionAuthChallengePerformDefaultHandling
處理請(qǐng)求堂油,就好像代理沒有提供一個(gè)代理方法來處理認(rèn)證請(qǐng)求 -
NSURLSessionAuthChallengeRejectProtectionSpace
拒接認(rèn)證請(qǐng)求修档。基于服務(wù)器響應(yīng)的認(rèn)證類型府框,URL加載類可能會(huì)多次調(diào)用代理方法吱窝。
SecTrustRef
表示需要驗(yàn)證的信任對(duì)象(Trust Object),在此指的是challenge.protectionSpace.serverTrust。包含待驗(yàn)證的證書和支持的驗(yàn)證方法等院峡。
SecTrustResultType
表示驗(yàn)證結(jié)果兴使。其中 kSecTrustResultProceed表示serverTrust驗(yàn)證成功,且該驗(yàn)證得到了用戶認(rèn)可(例如在彈出的是否信任的alert框中選擇always trust)照激。 kSecTrustResultUnspecified表示 serverTrust驗(yàn)證成功发魄,此證書也被暗中信任了,但是用戶并沒有顯示地決定信任該證書俩垃。 兩者取其一就可以認(rèn)為對(duì)serverTrust驗(yàn)證成功励幼。
SecTrustEvaluate
函數(shù)內(nèi)部遞歸地從葉節(jié)點(diǎn)證書到根證書驗(yàn)證。使用系統(tǒng)默認(rèn)的驗(yàn)證方式驗(yàn)證Trust Object口柳,根據(jù)上述證書鏈的驗(yàn)證可知苹粟,系統(tǒng)會(huì)根據(jù)Trust Object的驗(yàn)證策略,一級(jí)一級(jí)往上啄清,驗(yàn)證證書鏈上每一級(jí)證書有效性六水。
對(duì)于非自簽名的證書,即使服務(wù)器返回的證書是信任的CA頒發(fā)的辣卒,假如有更強(qiáng)的安全要求掷贾,可以繼續(xù)對(duì)Trust Object進(jìn)行更嚴(yán)格的驗(yàn)證。常用的方式是在本地導(dǎo)入證書荣茫,并將導(dǎo)入的證書設(shè)置成需要參與驗(yàn)證的錨點(diǎn)證書想帅,再調(diào)用SecTrustEvaluate通過本地導(dǎo)入的證書來驗(yàn)證服務(wù)器證書是否是可信的。如果服務(wù)器證書是這個(gè)錨點(diǎn)證書對(duì)應(yīng)CA或者子CA頒發(fā)的啡莉,或服務(wù)器證書本身就是這個(gè)錨點(diǎn)證書港准,則證書信任通過。如下代碼:
NSString *cerPath = ...;//證書在本次的路徑
NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)cerPath);
self.trustedCertificates = @[CFBridgingRelease(certificate)];
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
NSURLCredential *credential = nil;
SecTrustRef trust = challenge.protectionSpace.serverTrust;
SecTrustResultType result;
//將之前導(dǎo)入的證書設(shè)置成下面驗(yàn)證的Trust Object的錨點(diǎn)證書
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)self.trustedCertificates);
//通過本地導(dǎo)入的證書來驗(yàn)證服務(wù)器返回的證書是否可信
OSStatus status = SecTrustEvaluate(trust, &result);
if (status == kSecTrustResultUnspecified || status == kSecTrustResultProceed) {
credential = [NSURLCredential credentialForTrust:trust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
}
}
completionHandler(disposition, credential);
}
自簽名證書
對(duì)于自簽名證書咧欣,這樣Trust Object中的服務(wù)器證書是不可信任的CA頒發(fā)的浅缸,直接使用SecTrustEvaluate驗(yàn)證是不會(huì)成功的。
可以采取下述簡(jiǎn)單代碼繞過HTTPS的驗(yàn)證:
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
綜上對(duì)非自建和自建證書驗(yàn)證過程的分析魄咕,可以總結(jié)如下:
- 獲取需要驗(yàn)證的信任對(duì)象(Trust Object)衩椒。對(duì)于NSURLSession來說,
是從delegate方法URLSession:task:didReceiveChallenge:completionHandler:
回調(diào)回來的參數(shù)challenge中獲取(challenge.protectionSpace.serverTrust) 哮兰。
使用系統(tǒng)默認(rèn)驗(yàn)證方式驗(yàn)證Trust Object毛萌。 - SecTrustEvaluate會(huì)根據(jù)Trust Object的驗(yàn)證策略,一級(jí)一級(jí)往上喝滞,驗(yàn)證證書鏈上每一級(jí)數(shù)字簽名的有效性阁将,從而評(píng)估證書的有效性。
- 如第二步驗(yàn)證通過了右遭,一般的安全要求下做盅,就可以直接驗(yàn)證通過缤削,進(jìn)入到下一步:使用Trust Object生成一份憑證([NSURLCredential credentialForTrust:serverTrust]),傳入block中 completionHandler(disposition, credential)處理吹榴,建立連接僻他。
- 假如有更強(qiáng)的安全要求,可以繼續(xù)對(duì)Trust Object進(jìn)行更嚴(yán)格的驗(yàn)證腊尚。常用的方式是在本地導(dǎo)入證書,驗(yàn)證Trust Object與導(dǎo)入的證書是否匹配满哪。
- 假如驗(yàn)證失敗婿斥,取消此次Challenge-Response Authentication驗(yàn)證流程,拒絕連接請(qǐng)求哨鸭。
- 假如是自建證書的民宿,則不使用第二步系統(tǒng)默認(rèn)的驗(yàn)證方式,因?yàn)樽越ㄗC書的根CA的數(shù)字簽名未在操作系統(tǒng)的信任列表中像鸡。
在AFNetwoking的AFURLSessionManager.m這個(gè)文件里實(shí)現(xiàn)了代理方法URLSession:task:didReceiveChallenge:completionHandler:
并且在代理方法里對(duì)服務(wù)器返回的證書做了驗(yàn)證活鹰。驗(yàn)證的方法是evaluateServerTrust:forDomain:(NSString *)domain
。
AFNetwoking提供了一個(gè)類AFSecurityPolicy
,它有一個(gè)屬性:
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
};
@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
這個(gè)枚舉值決定了按哪種方式來驗(yàn)證服務(wù)器返回的證書只估。默認(rèn)值是AFSSLPinningModeNone
志群。
- AFSSLPinningModeNone:這會(huì)通過SecTrustEvaluate來驗(yàn)證服務(wù)器返回的證書,對(duì)于CA認(rèn)證的證書可以選用這個(gè)值蛔钙;
- AFSSLPinningModeCertificate:如果使用了CA認(rèn)證的證書锌云,并且在本地也導(dǎo)入了證書作為錨點(diǎn)證書來驗(yàn)證服務(wù)器返回的證書,可以用這個(gè)值吁脱。