iOS實(shí)用教程之Https雙向認(rèn)證詳解
前言
年前的時(shí)候,關(guān)于蘋果要強(qiáng)制https的傳言四起,雖然結(jié)果只是一個(gè)“謠言”,但是很明顯的這是遲早會(huì)到來(lái)的,間接上加速了各公司加緊上https的節(jié)奏,對(duì)于iOS客戶端來(lái)說(shuō),上https需不需要改變一些東西取決于---------對(duì),就是公司有沒(méi)有錢。土豪公司直接買買買,iOS開發(fā)者只需要把http改成https完事。然而很不幸,我們?cè)跊](méi)錢的公司,選擇了自簽證書。雖然網(wǎng)上很多關(guān)于https的適配,然而很多都是已過(guò)時(shí)的,這里我們主要是講一下https雙向認(rèn)證。
【證書選擇】自簽
【網(wǎng)絡(luò)請(qǐng)求】原生NSURLSession或者AFNetworking3.0以上版本
【認(rèn)證方式】雙向認(rèn)證
Https雙向認(rèn)證過(guò)程
先來(lái)了解一下雙向認(rèn)證的大體過(guò)程:(圖片來(lái)自網(wǎng)絡(luò),如果是某位博主原創(chuàng)的請(qǐng)私信我)

下面我們一步步來(lái)實(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ù)傳來(lái)的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);
來(lái)執(zhí)行回調(diào)操作
關(guān)于UIWebView的Https雙向認(rèn)證
網(wǎng)上的資料大體上有幾種解決方法
1:跳過(guò)Https認(rèn)證(這還能跳過(guò)?沒(méi)試過(guò),不太靠譜)
2:中斷原有的請(qǐng)求步驟,將request拿出來(lái),下載完整的HTML代碼,讓webView加載該代碼(在單頁(yè)面展示的情況下基本滿足使用,但是在部分標(biāo)簽不是獨(dú)立跳轉(zhuǎn)https路徑的時(shí)候,將出現(xiàn)無(wú)法加載的情況,不是很好用)
- (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拿出來(lái),完成鑒權(quán)認(rèn)證之后,再讓webView重新請(qǐng)求該request(這種方式理論上好像可以,我試過(guò),沒(méi)有成功,可能我打開的方式不正確)
4、或許,您有更好的解決方案 - -
關(guān)于代碼
網(wǎng)上很多https雙向認(rèn)證的代碼,基本是一樣的,這里我們直接拿來(lái)用就可以,前提是我們不能單純copy,而是在理解其實(shí)現(xiàn)的基礎(chǔ)上,整合到工程中,遇到問(wèn)題解決思路清晰,而不是一臉懵逼。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
iOS小數(shù)取整的方法(ceil?floor?round)示例
這篇文章主要為大家介紹了iOS小數(shù)取整的方法(ceil?floor?round)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
詳解iOS的UI開發(fā)中控制器的創(chuàng)建方法
這篇文章主要介紹了iOS的UI開發(fā)中控制器的創(chuàng)建方法,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-11-11
WKWebview非全屏自動(dòng)播放h5視頻的實(shí)現(xiàn)方法(Swift、OC)
這篇文章主要給大家介紹了關(guān)于WKWebview非全屏自動(dòng)播放h5視頻的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
iOS本地推送簡(jiǎn)單實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了iOS本地推送簡(jiǎn)單實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09

