iOS用自簽名證書實現(xiàn)HTTPS請求的原理實例講解


更新:蘋果官方發(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的allowInvalidCertificatesvalidatesDomainName進行設置炮沐。
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 dispositionNSURLCredential *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上庞溜,使用時請注意在ViewControllerNetworkManager#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請求

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末漫试,一起剝皮案震驚了整個濱河市六敬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驾荣,老刑警劉巖外构,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異秘车,居然都是意外死亡典勇,警方通過查閱死者的電腦和手機劫哼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門叮趴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人权烧,你說我怎么就攤上這事眯亦∩烁龋” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵妻率,是天一觀的道長乱顾。 經常有香客問我,道長宫静,這世上最難降的妖魔是什么走净? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮孤里,結果婚禮上伏伯,老公的妹妹穿的比我還像新娘。我一直安慰自己捌袜,他們只是感情好说搅,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著虏等,像睡著了一般弄唧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上霍衫,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天候引,我揣著相機與錄音,去河邊找鬼敦跌。 笑死背伴,一個胖子當著我的面吹牛,可吹牛的內容都是我干的峰髓。 我是一名探鬼主播傻寂,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼携兵!你這毒婦竟也來了疾掰?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤徐紧,失蹤者是張志新(化名)和其女友劉穎静檬,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體并级,經...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡拂檩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了嘲碧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稻励。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出望抽,到底是詐尸還是另有隱情加矛,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布煤篙,位于F島的核電站斟览,受9級特大地震影響,放射性物質發(fā)生泄漏辑奈。R本人自食惡果不足惜苛茂,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸠窗。 院中可真熱鬧味悄,春花似錦、人聲如沸塌鸯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丙猬。三九已至涨颜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茧球,已是汗流浹背庭瑰。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留抢埋,地道東北人弹灭。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像揪垄,于是被迫代替她去往敵國和親穷吮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內容