iOS 9.0之后蘋果開始要求使用Https進(jìn)行通信。ATS是iOS9和OS X El Capitan的一個(gè)新特性。開啟該功能后,ATS對(duì)使用NSURLConnection, CFURL或NSURLSession 等APIs 進(jìn)行的網(wǎng)絡(luò)請(qǐng)求默認(rèn)強(qiáng)制使用HTTPS加密傳輸,目標(biāo)是提高Apple 操作系統(tǒng)以及應(yīng)用程序的安全性。蘋果公司官方文章指出,https必須符合ATS要求,服務(wù)器必須支持傳輸層安全(TLS)協(xié)議1.2以上版本;證書必須使用SHA256或更高的哈希算法簽名,并使用2048位以上RSA密鑰或256位以上ECC算法等等。
https概要
- https協(xié)議基于http(超文本傳輸協(xié)議),在http通信基礎(chǔ)上對(duì)傳輸報(bào)文進(jìn)行加密,主要是為了保證通信雙方的數(shù)據(jù)不被竊取。在通信時(shí),服務(wù)器和客戶端各自提供自己的憑證,驗(yàn)證自己的身份后進(jìn)行通信. 這樣能減少不受信任的三方竊取信息。
- PKI(公鑰基礎(chǔ)設(shè)施),是HTTPS的基礎(chǔ),PKI與非堆成秘鑰加密技術(shù)密切相關(guān),包括消息摘要,數(shù)字簽名,和加密服務(wù)。而數(shù)字證書以及證書機(jī)構(gòu)(CA -Certificate Authority)是PKI中重要的概念。
- 數(shù)字證書在https中也起著至關(guān)重要的作用, 它是一個(gè)計(jì)算機(jī)文件,一般由可信任的證書機(jī)構(gòu)頒發(fā),他包含了證書所有者的一般信息(公開秘鑰)。并且能夠證明這個(gè)公鑰確實(shí)是證書所有者所合法擁有的。這一點(diǎn)是有CA對(duì)數(shù)字證書的簽名來保證的(簽名用到了信息摘要以及非對(duì)稱加密算法,使用CA的私鑰加密),在這里我門需要使用CA的數(shù)字證書(包含CA的公鑰)來驗(yàn)證簽名的合法性,那么我門如何驗(yàn)證CA數(shù)字證書的合法性呢?CA的信任鏈可以解決這個(gè)問題。一個(gè)CA的證書是由上一級(jí)CA簽發(fā)的,因此它的合法性由它的上一級(jí)CA來驗(yàn)證,其中最頂層的證書機(jī)構(gòu)被稱為根CA,它的證書被稱為根證書,如果一個(gè)鏈條的根證書是合法可信的,那么我們就認(rèn)為在這個(gè)鏈條上的所有CA以及它們所簽發(fā)的證書都是合法可信的。于是,最終的問題就是如何保證根證書的合法性呢?原來,我們所使用的基本計(jì)算機(jī)軟件,比如瀏覽器和服務(wù)器軟件,都會(huì)內(nèi)置根CA的自簽名證書,只要我們使用的基本軟件可信,那么就能保證根CA證書的合法有效。
https圖解
- https雙向通信,只是在服務(wù)器身份確認(rèn)后,提供客戶端憑證再進(jìn)行一次客戶端的身份校驗(yàn)過程。首先客戶端和服務(wù)器需要經(jīng)過三次確認(rèn),生成二個(gè)隨機(jī)數(shù),并將服務(wù)器的證書提供給客戶端。前面兩個(gè)隨機(jī)數(shù)因?yàn)闀r(shí)明文傳輸?shù)?很容易給截取到,當(dāng)客戶端驗(yàn)證完服務(wù)器端的證書OK后,通過隨機(jī)數(shù)擴(kuò)展算法,按一定的規(guī)則生成第三個(gè)隨機(jī)數(shù),并用服務(wù)器的公鑰加密后發(fā)送給服務(wù)器.這樣客戶端和服務(wù)器均獲得三個(gè)隨機(jī)數(shù),從而可以生成雙方約定的會(huì)話秘鑰。 客戶端再將自己的證書發(fā)送給服務(wù)器端進(jìn)行驗(yàn)證,只要服務(wù)器驗(yàn)證通過,那么他們就開始使用這個(gè)隨機(jī)秘鑰進(jìn)行數(shù)據(jù)加密。實(shí)際上如果, 客戶端和服務(wù)器端協(xié)議版本號(hào)不一致,客戶端偏好設(shè)置中的加密算法列表里沒有服務(wù)器支持的任何一種加密算法,客戶端的憑證或服務(wù)器的憑證無法匹配,服務(wù)器域名不正確,或者證書過期等,都會(huì)導(dǎo)致通信失敗,這時(shí)候根據(jù)指定的警報(bào)協(xié)議 將相關(guān)的失敗信息通知到接收方。
-
下圖為https的單向通信的主要流程,實(shí)際要比這個(gè)要復(fù)雜的多(忽略上面客戶端憑證的校驗(yàn)過程)。
Snip20161204_8.png
https通信詳解
- HTTPS是工作于SSL層上的HTTP協(xié)議,SSL(安全套接層)工作于TCP層上,向應(yīng)用層提供了兩個(gè)基本的安全服務(wù),認(rèn)證和保密。 SSL有三個(gè)子協(xié)議, 握手協(xié)議,記錄協(xié)議,警報(bào)協(xié)議。
- 握手協(xié)議:
- 建立安全能力: 由客戶端發(fā)起,向服務(wù)器發(fā)送Client Hello消息,其中包含SSL版本,客戶端隨機(jī)數(shù)(用于生成秘鑰),會(huì)話號(hào),加密算法清單(客戶端所支持的加密算法),壓縮算法清單。服務(wù)器返回 Server Hello信息,其中包含SSL版本,服務(wù)器隨機(jī)數(shù)(用于生成秘鑰),會(huì)話號(hào),選擇加密算法,選擇的壓縮算法。
- 服務(wù)器認(rèn)證與秘鑰交換服務(wù)器是本階段發(fā)送信息的所有方,共三步
- 第一步 : 證書,服務(wù)器將數(shù)字證書以及CA證書鏈發(fā)給客戶端,客戶端由此獲得服務(wù)器公鑰;客戶端還可以再這一步驗(yàn)證服務(wù)器是否可信.如果不可信則可以停止鏈接。并提醒用戶注意。
- 第二步 : 服務(wù)器請(qǐng)求客戶端證書,客戶端認(rèn)證在SSL中是可選的,因此這一步也是可選的。
- 第三步 : 服務(wù)器握手完成,發(fā)送這個(gè)消息后,服務(wù)器等待客戶端的響應(yīng)。
- 客戶端認(rèn)證與秘鑰交換
- 客戶端是本階段的所有信息的發(fā)送方,分為三步
- 第一步 : 證書,客戶端將客戶端的數(shù)字證書發(fā)送給服務(wù)器,里面包含了客戶端的公鑰,通常來說這個(gè)證書應(yīng)該是由服務(wù)提供著分發(fā)給客戶端,由指定的CA簽發(fā)的,因此服務(wù)器可以驗(yàn)證客戶端證書的合法性,并決定是否繼續(xù)。
- 第二步 : 秘鑰交換,客戶端生成48字節(jié)的預(yù)備秘鑰,并用服務(wù)器端的公鑰加密,然后發(fā)送給服務(wù)器,這個(gè)預(yù)備秘鑰只有客戶端和服務(wù)器知道(與雙發(fā)在之后會(huì)話中使用的對(duì)成秘鑰相關(guān)),在這一步也間接的驗(yàn)證了服務(wù)器的合法性.因?yàn)橹挥袚碛信c證書對(duì)應(yīng)的私鑰才能解密出預(yù)備秘鑰。
- 第三步 : 證書驗(yàn)證,客戶端還需要向服務(wù)器驗(yàn)證自己是真正的客戶端(數(shù)字證書無法證明它就是客戶端,擁有閾公鑰對(duì)應(yīng)的私鑰才是關(guān)鍵),為此客戶端把預(yù)備秘鑰,客戶隨機(jī)數(shù),服務(wù)器的隨記數(shù)組和起來,用私鑰對(duì)結(jié)果進(jìn)行簽名,發(fā)送給服務(wù)器,服務(wù)器利用客戶端公鑰就能夠的到原始的數(shù)據(jù),用來驗(yàn)證客戶端的真實(shí)性。(這里主要是對(duì) 客戶端隨記數(shù),服務(wù)器隨記數(shù),預(yù)備秘鑰)這個(gè)三個(gè)要素進(jìn)行判定是否有人竊取和串改。
- 完成: 在這個(gè)階段,客戶端和服務(wù)器各自獨(dú)立生成相同的主秘鑰和對(duì)成秘鑰,主秘鑰和對(duì)成秘鑰只有它們自己知道。主秘鑰和對(duì)成秘鑰是由 預(yù)備秘鑰,客戶端隨機(jī)數(shù)和服務(wù)器隨機(jī)數(shù)組和后,經(jīng)過消息摘要算法生成。
- 握手協(xié)議:
- SSL握手完成之后,就會(huì)進(jìn)入回話階段:客戶端和服務(wù)器使用握手協(xié)議中生成的對(duì)成秘鑰進(jìn)行加密和解密以保證通信的安全。
- 總的來說, 如果鑰完成HTTPS的雙向認(rèn)證需要以下秘鑰和證書:
- 服務(wù)器端: 1.服務(wù)器私鑰, 2.由CA前發(fā)的含有服務(wù)器公鑰的數(shù)字證書, 3.CA的數(shù)字證書,在雙向驗(yàn)證過程中通常服務(wù)器可以自己作為證書機(jī)構(gòu),并且由服務(wù)器CA前發(fā)服務(wù)器證書和客戶端證書。
- 客服端: 1.客戶端私鑰, 2. 由CA簽發(fā)的含有客戶端公鑰的數(shù)字證書。為了避免中間人攻擊,客戶端還需要內(nèi)置服務(wù)器證書,用來驗(yàn)證所連接的服務(wù)器是否是指定的服務(wù)器。(通常在一些服務(wù)器的網(wǎng)站或者app中,客服端憑證校驗(yàn)是默認(rèn)省略的,單向安全通信的,只對(duì)服務(wù)器的身份進(jìn)行校驗(yàn))。
https實(shí)踐
- 在Apple Reference《Certificate,Key,and Trust Services Programming Guide》提供了https驗(yàn)證的框架, security.該框架包含了 https通信中信任對(duì)象, 同時(shí)也提供https加密中SAH,MD5,RSA,AES等算法的支持。
- 基于AFNetworking https單向驗(yàn)證,系統(tǒng)框架已經(jīng)做了非常多的簡(jiǎn)化步驟,在AFSecurity這個(gè)庫中,框架內(nèi)部已經(jīng)做好了相關(guān)的判定,比如根據(jù)是否為私有證書,將提供的 證書對(duì)象設(shè)置為錨證書,對(duì)服務(wù)器返回的 信任對(duì)象進(jìn)行評(píng)估,如果通過則進(jìn)行安全通信,反之則取消通信。
+ (AFSecurityPolicy*)configSecurityPolicy {
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"https" ofType:@"cer"];//證書的路徑
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
AFSecurityPolicy* securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.allowInvalidCertificates = YES; /**如果采用三方頒發(fā)的證書,則不使用自建證書驗(yàn)證服務(wù)器,由三方機(jī)構(gòu)驗(yàn)證*/
securityPolicy.validatesDomainName = YES;
securityPolicy.pinnedCertificates = [NSSet setWithObject:certData];
mgr.securityPolicy = securityPolicy;
return securityPolicy;
}
- 基于AFNetworking雙向通信。
- 在AFNetworking中進(jìn)行雙向通信,需要我們自己對(duì)挑戰(zhàn)認(rèn)證進(jìn)行定義,其中服務(wù)器身份驗(yàn)證部分,我們?nèi)匀豢梢匝赜蒙厦娴?security對(duì)象進(jìn)行自動(dòng)校驗(yàn),但是客戶端驗(yàn)證,就需要我們進(jìn)行實(shí)現(xiàn)了。
- AFNetworking框架已經(jīng)為我們講調(diào)整驗(yàn)證的方法是用回調(diào)Block抽離出來。在使用時(shí)候我們只需鑰注冊(cè)該回調(diào)block,在對(duì)應(yīng)的block中實(shí)現(xiàn)當(dāng)收到驗(yàn)證服務(wù)器端憑證和需要提供客服端憑證的方法進(jìn)行處理.相關(guān)代碼實(shí)現(xiàn)方式如下.
- 服務(wù)端驗(yàn)證: 首先需要對(duì)挑戰(zhàn)challege對(duì)象的保護(hù)空間進(jìn)行判定,獲取當(dāng)前的驗(yàn)證方式,如果是對(duì)服務(wù)器驗(yàn)證,則取出serverTrust對(duì)象,并通過設(shè)定錨證書,與我們定義的服務(wù)器證書的信任對(duì)象進(jìn)行比對(duì)和評(píng)估,判定是否正確,如果正確,則告訴challege挑戰(zhàn)發(fā)起者,下次繼續(xù)使用該憑證作為服務(wù)器的身份驗(yàn)證。
- 客戶端驗(yàn)證: 將客服端憑證對(duì)象,通常都是由p12文件生成, 先將它轉(zhuǎn)換為CFData類型,再通過 valueForKey的方式,一層一層的將其撥開,取到最后的秘鑰,并生成NSURLCredential憑證對(duì)象。
- AFNetworking框架最終生成的憑證對(duì)象,以及驗(yàn)證方式,通過block的回調(diào)的方式在 delegate挑戰(zhàn)認(rèn)證的方法中回調(diào)出去。
+ (void)configSSLChallage {
AFHTTPSessionManager* mgr = [self shareInstance];
__weak typeof(*&mgr)weakmgr = mgr;
[mgr setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing _Nullable * _Nullable credential) {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__autoreleasing NSURLCredential *_credential =nil;
//server 端驗(yàn)證:
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSLog(@"authorMethod:%@",challenge.protectionSpace.authenticationMethod);
if([weakmgr.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host] ) {
_credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if(credential) {
disposition =NSURLSessionAuthChallengeUseCredential;
} else {
disposition =NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
//client 客戶端驗(yàn)證:
NSLog(@"authorMethod:%@",challenge.protectionSpace.authenticationMethod);
// client authentication
SecIdentityRef identity = NULL;
SecTrustRef trust = NULL;
NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];
NSFileManager *fileManager =[NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:p12])
{
NSLog(@"client.p12:not exist");
}
else
{
NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
if ([PLMNetWorkTool extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
{
if (extractIdentityAndTrust((__bridge CFDataRef)(PKCS12Data), &identity, &trust)== noErr) {
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate(identity, &certificate);
const void*certs[] = {certificate};
CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
_credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
disposition =NSURLSessionAuthChallengeUseCredential;
}
}
}
}
*credential = _credential;
return disposition;
}];
}
總結(jié): https涉及的內(nèi)容較多,如果不明白其中的具體原理,僅僅靠搬運(yùn)一份代碼,那么一段出現(xiàn)問題將是非常致命的。理解其中的緣由對(duì)于我們應(yīng)對(duì)https證書偽造,域名劫持,或是https會(huì)話秘鑰擴(kuò)展,https安全通信雙層加固非常重要。
參考文獻(xiàn): http://baike.baidu.com/link?url=QDGRYuHehLYwvUSNX_vH7xo8tQ8CJoDSSKeLP_PWzmohvk5E1E6RegmRGB0hERJW_0MqTBJn6sp6h65hLi_04a
http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html