iOS實(shí)用篇:Https雙向認(rèn)證

年前的時(shí)候,關(guān)于蘋果要強(qiáng)制https的傳言四起,雖然結(jié)果只是一個(gè)“謠言”,但是很明顯的這是遲早會(huì)到來的,間接上加速了各公司加緊上https的節(jié)奏,對(duì)于iOS客戶端來說,上https需不需要改變一些東西取決于---------對(duì),就是公司有沒有錢。土豪公司直接買買買,iOS開發(fā)者只需要把http改成https完事。然而很不幸,我們?cè)跊]錢的公司,選擇了自簽證書。雖然網(wǎng)上很多關(guān)于https的適配,然而很多都是已過時(shí)的,這里我們主要是講一下https雙向認(rèn)證。

【證書選擇】自簽
【網(wǎng)絡(luò)請(qǐng)求】原生NSURLSession或者AFNetworking3.0以上版本
【認(rèn)證方式】雙向認(rèn)證

Https雙向認(rèn)證過程

先來了解一下雙向認(rèn)證的大體過程:(圖片來自網(wǎng)絡(luò),如果是某位博主原創(chuàng)的請(qǐng)私信我)

httpsLine.png

下面我們一步步來實(shí)現(xiàn)

1、設(shè)置服務(wù)端證書

    NSString *certFilePath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];
    NSData *certData = [NSData dataWithContentsOfFile:certFilePath];
    NSSet *certSet = [NSSet setWithObject:certData];
    AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:certSet];
    policy.allowInvalidCertificates = YES;
    policy.validatesDomainName = NO;
    self.afnetworkingManager.securityPolicy = policy;

2、處理挑戰(zhàn)

原生的NSURLSession是在

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(nonnull NSURLAuthenticationChallenge *)challenge completionHandler:(nonnull void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler 

代理方法里面處理挑戰(zhàn)的,再看看AFNetworking在該代理方法里處理的代碼

    if (self.taskDidReceiveAuthenticationChallenge) {
        disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
    } else {
        ...
    }

我們只需要給它傳遞一個(gè)處理的block

[self.afnetworkingManager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
        ...
}

根據(jù)傳來的challenge生成disposition(應(yīng)對(duì)挑戰(zhàn)的方式)和credential(客戶端生成的挑戰(zhàn)證書)

3、服務(wù)端認(rèn)證

當(dāng)challenge的認(rèn)證方法為NSURLAuthenticationMethodServerTrust時(shí),需要客戶端認(rèn)證服務(wù)端證書

//評(píng)估服務(wù)端安全性
if([weakSelf.afnetworkingManager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                //創(chuàng)建憑據(jù)
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                if(credential) {
                    disposition =NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition =NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }

4、客戶端認(rèn)證

認(rèn)證完服務(wù)端后,需要認(rèn)證客戶端
由于是雙向認(rèn)證,這一步是必不可省的

            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 ([[weakSelf class]extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
                {
                    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;
                }
            }
+ (BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
    OSStatus securityError = errSecSuccess;
    //client certificate password
    NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"your p12 file pwd"
                                                                 forKey:(__bridge id)kSecImportExportPassphrase];
    
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);
    
    if(securityError == 0) {
        CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
        const void*tempIdentity =NULL;
        tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
        *outIdentity = (SecIdentityRef)tempIdentity;
        const void*tempTrust =NULL;
        tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
        *outTrust = (SecTrustRef)tempTrust;
    } else {
        NSLog(@"Failedwith error code %d",(int)securityError);
        return NO;
    }
    return YES;
}

原生NSURLSession雙向認(rèn)證

在原生的代理方法里面認(rèn)證就行,代碼基本和AFNetworking的一致,注意最后需要調(diào)用

 completionHandler(NSURLSessionAuthChallengeUseCredential, credential);

來執(zhí)行回調(diào)操作

關(guān)于UIWebView的Https雙向認(rèn)證

網(wǎng)上的資料大體上有幾種解決方法
1:跳過Https認(rèn)證(這還能跳過?沒試過,不太靠譜)
2:中斷原有的請(qǐng)求步驟,將request拿出來,下載完整的HTML代碼,讓webView加載該代碼(在單頁面展示的情況下基本滿足使用,但是在部分標(biāo)簽不是獨(dú)立跳轉(zhuǎn)https路徑的時(shí)候,將出現(xiàn)無法加載的情況,不是很好用)

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSString * urlString = [request.URL absoluteString];
    if ([urlString containsString:URL_API_BASE]) {
        [[SUHTTPOperationManager manager]REQUEST:request progress:nil handler:^(BOOL isSucc, id responseObject, NSError *error) {
            NSString * htmlString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
            BASE_INFO_FUN(@"下載HTML完畢");
            [self loadHTMLString:htmlString baseURL:nil];
        }];
        return NO;
    }
    return YES;
}

3、中斷原有的請(qǐng)求步驟,將request拿出來,完成鑒權(quán)認(rèn)證之后,再讓webView重新請(qǐng)求該request(這種方式理論上好像可以,我試過,沒有成功,可能我打開的方式不正確)
4、或許,您有更好的解決方案 - -

關(guān)于代碼

網(wǎng)上很多https雙向認(rèn)證的代碼,基本是一樣的,這里我們直接拿來用就可以,前提是我們不能單純copy,而是在理解其實(shí)現(xiàn)的基礎(chǔ)上,整合到工程中,遇到問題解決思路清晰,而不是一臉懵逼。

PS:大半年沒更新文章了,也沒怎么上簡書,發(fā)現(xiàn)還是有很多人支持的,有朋友的私信也沒回復(fù),表示歉意。

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

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

  • 本想在這篇文章中單獨(dú)寫AFNetworking 3.0中AFSecurityPolicy的源碼閱讀筆記的。但隨著源...
    WeiHing閱讀 2,710評(píng)論 1 13
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 寫在前面: 1.相關(guān)https具體內(nèi)容本篇就不再描述。 2.前幾天公司項(xiàng)目要求配置https雙向認(rèn)證,由于是銀行業(yè)...
    葛高召閱讀 2,078評(píng)論 1 5
  • 還是習(xí)慣去你的朋友圈,去尋找你的足跡。只是我不再留言,不再留下痕跡,不想讓彼此更累。心還是痛的,默默為我們的無緣而...
    D038_禮恩_佛山閱讀 191評(píng)論 0 3
  • 夏日酷暑,頭頂著烈日灼心,不曾有褪去熱浪的意思。就連傍晚的風(fēng),也如剛從鍋爐里鉆出來的一樣。摸著發(fā)燙的臺(tái)階,我硬著頭...
    冰雪傾心閱讀 298評(píng)論 4 4

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