您好,登錄后才能下訂單哦!
前言
年前的時候,關于蘋果要強制https的傳言四起,雖然結果只是一個“謠言”,但是很明顯的這是遲早會到來的,間接上加速了各公司加緊上https的節奏,對于iOS客戶端來說,上https需不需要改變一些東西取決于---------對,就是公司有沒有錢。土豪公司直接買買買,iOS開發者只需要把http改成https完事。然而很不幸,我們在沒錢的公司,選擇了自簽證書。雖然網上很多關于https的適配,然而很多都是已過時的,這里我們主要是講一下https雙向認證。
【證書選擇】自簽
【網絡請求】原生NSURLSession或者AFNetworking3.0以上版本
【認證方式】雙向認證
Https雙向認證過程
先來了解一下雙向認證的大體過程:(圖片來自網絡,如果是某位博主原創的請私信我)
下面我們一步步來實現
1、設置服務端證書
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、處理挑戰
原生的NSURLSession是在
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(nonnull NSURLAuthenticationChallenge *)challenge completionHandler:(nonnull void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
代理方法里面處理挑戰的,再看看AFNetworking在該代理方法里處理的代碼
if (self.taskDidReceiveAuthenticationChallenge) { disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential); } else { ... }
我們只需要給它傳遞一個處理的block
[self.afnetworkingManager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) { ... }
根據傳來的challenge生成disposition(應對挑戰的方式)和credential(客戶端生成的挑戰證書)
3、服務端認證
當challenge的認證方法為NSURLAuthenticationMethodServerTrust時,需要客戶端認證服務端證書
//評估服務端安全性 if([weakSelf.afnetworkingManager.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; }
4、客戶端認證
認證完服務端后,需要認證客戶端
由于是雙向認證,這一步是必不可省的
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雙向認證
在原生的代理方法里面認證就行,代碼基本和AFNetworking的一致,注意最后需要調用
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
來執行回調操作
關于UIWebView的Https雙向認證
網上的資料大體上有幾種解決方法
1:跳過Https認證(這還能跳過?沒試過,不太靠譜)
2:中斷原有的請求步驟,將request拿出來,下載完整的HTML代碼,讓webView加載該代碼(在單頁面展示的情況下基本滿足使用,但是在部分標簽不是獨立跳轉https路徑的時候,將出現無法加載的情況,不是很好用)
- (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、中斷原有的請求步驟,將request拿出來,完成鑒權認證之后,再讓webView重新請求該request(這種方式理論上好像可以,我試過,沒有成功,可能我打開的方式不正確)
4、或許,您有更好的解決方案 - -
關于代碼
網上很多https雙向認證的代碼,基本是一樣的,這里我們直接拿來用就可以,前提是我們不能單純copy,而是在理解其實現的基礎上,整合到工程中,遇到問題解決思路清晰,而不是一臉懵逼。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。