更新:蘋果官方發(fā)布公告,暫緩HTTPS的強制性要求斟叼,具體要求時間再另行通知斯嚎,要配置的開發(fā)者們可以暫緩一口氣了。
在16年的WWDC中俘陷,Apple已表示將從2017年1月1日起,所有新提交的App必須強制性應用HTTPS協(xié)議來進行網(wǎng)絡請求观谦。默認情況下非HTTPS的網(wǎng)絡訪問是禁止的并且不能再通過簡單粗暴的向Info.plist中添加NSAllowsArbitraryLoads
設置繞過ATS(App Transport Security)的限制(否則須在應用審核時進行說明并很可能會被拒)拉盾。所以還未進行相應配置的公司需要盡快將升級為HTTPS的事項提上進程了。
本文將簡述HTTPS及配置數(shù)字證書的原理并以配置實例和出現(xiàn)的問題進行說明豁状,希望能對你提供幫助捉偏。(比心~)
HTTPS:
簡單來說,HTTPS就是HTTP協(xié)議上再加一層加密處理的SSL協(xié)議,即HTTP安全版泻红。相比HTTP夭禽,HTTPS可以保證內容在傳輸過程中不會被第三方查看、及時發(fā)現(xiàn)被第三方篡改的傳輸內容谊路、防止身份冒充讹躯,從而更有效的保證網(wǎng)絡數(shù)據(jù)的安全。
HTTPS客戶端與服務器交互過程:
1缠劝、 客戶端第一次請求時潮梯,服務器會返回一個包含公鑰的數(shù)字證書給客戶端;
2剩彬、 客戶端生成對稱加密密鑰并用其得到的公鑰對其加密后返回給服務器酷麦;
3矿卑、 服務器使用自己私鑰對收到的加密數(shù)據(jù)解密喉恋,得到對稱加密密鑰并保存;
4母廷、 然后雙方通過對稱加密的數(shù)據(jù)進行傳輸轻黑。
數(shù)字證書:
在HTTPS客戶端與服務器第一次交互時,服務端返回給客戶端的數(shù)字證書是讓客戶端驗證這個數(shù)字證書是不是服務端的琴昆,證書所有者是不是該服務器氓鄙,確保數(shù)據(jù)由正確的服務端發(fā)來,沒有被第三方篡改业舍。數(shù)字證書可以保證數(shù)字證書里的公鑰確實是這個證書的所有者(Subject)的抖拦,或者證書可以用來確認對方身份升酣。證書由公鑰、證書主題(Subject)态罪、數(shù)字簽名(digital signature)等內容組成噩茄。其中數(shù)字簽名就是證書的防偽標簽,目前使用最廣泛的SHA-RSA加密复颈。
證書一般分為兩種:
一種是向權威認證機構購買的證書绩聘,服務端使用該種證書時,因為蘋果系統(tǒng)內置了其受信任的簽名根證書耗啦,所以客戶端不需額外的配置凿菩。為了證書安全,在證書發(fā)布機構公布證書時帜讲,證書的指紋算法都會加密后再和證書放到一起公布以防止他人偽造數(shù)字證書衅谷。而證書機構使用自己的私鑰對其指紋算法加密,可以用內置在操作系統(tǒng)里的機構簽名根證書來解密舒帮,以此保證證書的安全会喝。
另一種是自己制作的證書,即自簽名證書玩郊。好處是不需要花錢購買肢执,但使用這種證書是不會受信任的,所以需要我們在代碼中將該證書配置為信任證書译红。這就是本文的主要目的预茄。
具體實現(xiàn)
我們在使用自簽名證書來實現(xiàn)HTTPS請求時,因為不像機構頒發(fā)的證書一樣其簽名根證書在系統(tǒng)中已經內置了侦厚,所以我們需要在App中內置自己服務器的簽名根證書來驗證數(shù)字證書耻陕。
首先將服務端生成的.cer格式的根證書添加到項目中,注意在添加證書要一定要記得勾選要添加的targets刨沦。這里有個地方要注意:蘋果的ATS要求服務端必須支持TLS 1.2或以上版本诗宣;必須使用支持前向保密的密碼;證書必須使用SHA-256或者更好的簽名hash算法來簽名想诅,如果證書無效召庞,則會導致連接失敗。由于我在生成的根證書時簽名hash算法低于其要求来破,在配置完請求時一直報NSURLErrorServerCertificateUntrusted = -1202錯誤篮灼,希望大家可以注意到這一點。
本文使用AFNetworking 3.0來配置證書校驗徘禁。其中AFSecurityPolicy類中封裝了證書校驗的過程诅诱。
AFSecurityPolicy分三種驗證模式:
1、AFSSLPinningModeNone:只驗證證書是否在新人列表中
2送朱、AFSSLPinningModeCertificate:驗證證書是否在信任列表中娘荡,然后再對比服務端證書和客戶端證書是否一致
3干旁、 AFSSLPinningModePublicKey:只驗證服務端與客戶端證書的公鑰是否一致
這里我們選第二種模式,并且對AFSecurityPolicy的allowInvalidCertificates
和 validatesDomainName
進行設置炮沐。
AFSecurityPolicy具體配置代碼如下:
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.allowInvalidCertificates = YES; //是否允許使用自簽名證書
securityPolicy.validatesDomainName = NO; //是否需要驗證域名
self.manager = [AFHTTPSessionManager manager];
self.manager.responseSerializer = [AFHTTPResponseSerializer serializer];
self.manager.securityPolicy = securityPolicy;
服務端在接收到客戶端請求時會有的情況需要驗證客戶端證書疤孕,要求客戶端提供合適的證書,再決定是否返回數(shù)據(jù)央拖。這種情況即為質詢(challenge)認證,雙方進行公鑰和私鑰的驗證祭阀。
為實現(xiàn)客戶端驗證,manager須設置需要身份驗證回調的方法:
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block
并實現(xiàn)具體代碼來替換AFNetworking的默認實現(xiàn)鲜戒。其中參數(shù)challenge
為身份驗證質詢专控,block返回對身份驗證請求質詢的配置。
在接受到質詢后遏餐,客戶端要根據(jù)服務端傳來的challenge來生成所需的NSURLSessionAuthChallengeDisposition disposition
和NSURLCredential *credential
伦腐。disposition指定應對這個質詢的方法,而credential是客戶端生成的質詢證書失都,注意只有challenge中認證方法為NSURLAuthenticationMethodServerTrust的時候柏蘑,才需要生成挑戰(zhàn)證書。最后回應服務器的質詢粹庞。
具體實現(xiàn)代碼如下:
[self.manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential) {
// 獲取服務器的trust object
SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
//導入自簽名證書
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"ca" ofType:@"cer"];
NSData* caCert = [NSData dataWithContentsOfFile:cerPath];
NSArray *cerArray = @[caCert];
weakSelf.manager.securityPolicy.pinnedCertificates = cerArray;
SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
NSCAssert(caRef != nil, @"caRef is nil");
NSArray *caArray = @[(__bridge id)(caRef)];
NSCAssert(caArray != nil, @"caArray is nil");
OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
SecTrustSetAnchorCertificatesOnly(serverTrust,NO);
NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed");
//選擇質詢認證的處理方式
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__autoreleasing NSURLCredential *credential = nil;
//NSURLAuthenticationMethodServerTrust質詢認證方式
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
//基于客戶端的安全策略來決定是否信任該服務器咳焚,不信任則不響應質詢 。
if ([weakSelf.manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
//創(chuàng)建質詢證書
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
//確認質詢方式
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
//取消質詢
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
return disposition;
}];
詳細代碼發(fā)布在了github上庞溜,使用時請注意在ViewController
和NetworkManager
中#Warning
的提示革半,將證書拖入項目并在NetworkManager
中添加證書名稱,在ViewController
添加自己的URL流码。
文章中如有錯誤和疏漏又官,歡迎留言指教討論。
參考文章:
數(shù)字證書原理
Certificate, Key, and Trust Services Tasks for iOS
關于 iOS 10 中 ATS 的問題
App Transport Security(ATS)
AFNetworking之于https認證
iOS使用自簽名證書實現(xiàn)HTTPS請求