iOS中HTTP/HTTPS授權(quán)訪問(二)

背景

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

HTTPS認(rèn)證流程.png

第一步:客戶端向服務(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é)束了,感謝閱讀.

參考博文:

http://blog.csdn.net/tencent_bugly/article/details/54572899

http://www.itdecent.cn/p/a84237b07611

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容