背景
在網(wǎng)絡(luò)請求中有http和https請求兩種,兩者的解釋可以通過Google去了解下,這里不再做過多的描述.大致說一下https,這是http請求的升級(jí)版,具有良好的加密功能,在客戶端向服務(wù)端發(fā)起https請求時(shí)需要進(jìn)行一系列的處理,過程如下:

第一步:客戶端向服務(wù)端發(fā)起https請求(圖中1).
第二步:從圖中可以看出這個(gè)階段包含4個(gè)過程(圖中2,3,4,5)有的服務(wù)端是單條發(fā)送,有的是合并一起發(fā)送.服務(wù)端返回協(xié)商的信息結(jié)果,包括選擇使用的協(xié)議版本,選擇的加密套件,選擇的壓縮算法、隨機(jī)數(shù)random_S等,其中隨機(jī)數(shù)用于后續(xù)的密鑰協(xié)商。服務(wù)器也會(huì)配置并返回對應(yīng)的證書鏈Certificate,用于身份驗(yàn)證與密鑰交換.然后會(huì)發(fā)送ServerHelloDone信息用于通知客戶端信息發(fā)送結(jié)束(這里在上一篇文章中說到的delegate會(huì)被調(diào)用)。
第三步:這一步是由客戶端收到服務(wù)端返回的信息后要進(jìn)行解析,校驗(yàn),在通過校驗(yàn)后需要返回給服務(wù)端一個(gè)帶有證書加密的隨機(jī)值(圖中6,7,8),這個(gè)隨機(jī)值的作用就是讓服務(wù)端和客戶端建立一條由這個(gè)隨機(jī)值建立的秘密通道,以后所有的信息都在這條秘密通道中傳輸,并且用這個(gè)隨機(jī)值來進(jìn)行加解密(圖中9,10).
如果看過AFNetworking的話都知道它把服務(wù)端返回的權(quán)限認(rèn)證做了如下處理:
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
//權(quán)限處理類型為 默認(rèn)
/*
NSURLSessionAuthChallengePerformDefaultHandling:默認(rèn)方式處理
NSURLSessionAuthChallengeUseCredential:使用指定的證書
NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消權(quán)限認(rèn)證
*/
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
// sessionDidReceiveAuthenticationChallenge是自定義方法,用來如何應(yīng)對服務(wù)器端的認(rèn)證挑戰(zhàn)
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
NSURLAuthenticationMethodServerTrust
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.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 {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
整段代碼的重點(diǎn)在于[self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]這個(gè)self.securityPolicy是AFSecurityPolicy類,它將https請求的驗(yàn)證做了一個(gè)封裝,那么如果沒有這個(gè)封裝,或者我們在使用NSURLSession的時(shí)候應(yīng)該怎么處理呢?
如果是https請求的話challenge.protectionSpace.authenticationMethod肯定是NSURLAuthenticationMethodServerTrust這個(gè)字段,那么一般情況下我們需要做如下處理:
//1
SecTrustRef trust = challenge.protectionSpace.serverTrust;
SecTrustResultType resultType;
NSURLCredential *credential = nil;
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
//2
OSStatus status = SecTrustEvaluate(trust, &result);
//3
if (status == errSecSuccess &&
(result == kSecTrustResultProceed ||
result == kSecTrustResultUnspecified))
{
credential = [NSURLCredential credentialForTrust:trust];
if (credential)
{
disposition = NSURLSessionAuthChallengeUseCredential;
}
else
{
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
else
{
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
//4
if (completionHandler) {
completionHandler(disposition, credential);
}
詳解如下:
1.我們根據(jù)challenge.protectionSpace.serverTrust創(chuàng)建一個(gè)SecTrustRef對象以及后面要用到的鑒定結(jié)果類型resultType,證書憑證credential,后續(xù)處理方法disposition.
2.使用證書校驗(yàn)方法SecTrustEvaluate(serverTrust對象,校驗(yàn)結(jié)果)對服務(wù)端返回的serverTrust進(jìn)行校驗(yàn)并且返回校驗(yàn)結(jié)果status.
3.對返回值進(jìn)行處理,如果滿足條件則使用serverTrust創(chuàng)建credential.
4.將credential以及disposition回傳給服務(wù)器.
在iOS中HTTP/HTTPS授權(quán)訪問(一)中已經(jīng)對NSURLCredential,NSURLSessionAuthChallengeDisposition做了詳細(xì)的介紹,這里主要說下以下幾個(gè):
SecTrustRef
這是一個(gè)需要驗(yàn)證的信任對象,包含待驗(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)為對serverTrust驗(yàn)證成功。
SecTrustEvaluate
證書校驗(yàn)函數(shù),在函數(shù)的內(nèi)部遞歸地從葉節(jié)點(diǎn)證書到根證書驗(yàn)證。需要驗(yàn)證證書本身的合法性(驗(yàn)證簽名完整性,驗(yàn)證證書有效期等);驗(yàn)證證書頒發(fā)者的合法性(查找頒發(fā)者的證書并檢查其合法性,這個(gè)過程是遞歸的).而遞歸的終止條件是證書驗(yàn)證過程中遇到了錨點(diǎn)證書(錨點(diǎn)證書:嵌入到操作系統(tǒng)中的根證書,這個(gè)根證書是權(quán)威證書頒發(fā)機(jī)構(gòu)頒發(fā)的自簽名證書).
上面所說的只是一般的校驗(yàn)方法,那么在有的客戶端中,為了確定服務(wù)端返回的證書是否是自己所需要的證書,這時(shí)我們需要在客戶端中導(dǎo)入本地證書.整個(gè)過程代碼如下:
//本地導(dǎo)入證書
NSString *path = @"證書路徑";
NSData *certificateData = [NSData dataWithContentsOfFile:path];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData);
NSArray *certificateArray = @[CFBridgingRelease(certificate)];
SecTrustRef trust = challenge.protectionSpace.serverTrust;
SecTrustResultType result;
NSURLCredential *credential = nil;
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)certificateArray);
OSStatus status = SecTrustEvaluate(trust, &result);
if (status == errSecSuccess &&
(result == kSecTrustResultProceed ||
result == kSecTrustResultUnspecified))
{
credential = [NSURLCredential credentialForTrust:trust];
if (credential)
{
disposition = NSURLSessionAuthChallengeUseCredential;
}
else
{
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
else
{
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
if (completionHandler) {
completionHandler(disposition, credential);
}
相較于一般情況處理而言,這里多了一個(gè)證書導(dǎo)入,還有一個(gè)SecTrustSetAnchorCertificates(serverTrust對象, 本地證書數(shù)組)將本地證書數(shù)組設(shè)置成需要參與驗(yàn)證的錨點(diǎn)證書.最后還是通過SecTrustEvaluate()方法進(jìn)行校驗(yàn),假如驗(yàn)證的數(shù)字證書是這個(gè)錨點(diǎn)證書的子節(jié)點(diǎn),即驗(yàn)證的數(shù)字證書是由錨點(diǎn)證書對應(yīng)CA或子CA簽發(fā)的,或是該證書本身,則信任該證書.
結(jié)語
其實(shí)除了與本地證書校驗(yàn)以外,還可以將服務(wù)端回傳的證書公鑰與本地證書的公鑰進(jìn)行比對,這里就不給出示例了,詳細(xì)可以參考AFNetworking中的AFSecurityPolicy類.
整個(gè)iOS中的HTTP/HTTPS授權(quán)訪問到此就結(jié)束了,感謝閱讀.
參考博文: