寫這篇文章的目的
之前在做一個(gè)奇葩的定制,遇到提示“無(wú)法驗(yàn)證服務(wù)器的身份,可能鏈接到偽裝服務(wù)器”的錯(cuò)誤,然而我自己這邊的demo沒(méi)有出現(xiàn)這個(gè)問(wèn)題,一度表示很無(wú)奈。還有一個(gè)是使用SDWebImage加載網(wǎng)絡(luò)圖片時(shí),圖片下載失敗(圖片地址也是https),還有一個(gè)是使用UIImageView+AFNetworking加載圖片時(shí),請(qǐng)求被cancel。跟客戶溝通了很久,才解決了以上問(wèn)題,所以想寫篇文章記錄一下。
什么是SSL
SSL證書是數(shù)字證書的一種,也稱為SSL服務(wù)器證書,SSL證書通過(guò)在客戶端瀏覽器和Web服務(wù)器建立一條SSL安全通道,實(shí)現(xiàn)數(shù)據(jù)信息在客戶端和服務(wù)器之間的加密傳輸,可以防止數(shù)據(jù)信息的泄露,保證了雙方傳遞信息的安全性,而且用戶可以通過(guò)服務(wù)器證書驗(yàn)證他所訪問(wèn)的網(wǎng)站是否是真實(shí)可靠。
蘋果ATS對(duì)SSL做了以下要求:
- 使用SHA2級(jí)別的證書簽名算法,例如SHA-256, SHA-512等;
- 證書公鑰算法使用RSA 2048位及以上,或使用更高的算法ECC 256加密算法;
從權(quán)威機(jī)構(gòu)認(rèn)證過(guò)的證書一般都符合蘋果ATS要求,自建證書基本都需要APP去繞過(guò)證書校驗(yàn)的步驟。符合要求的很好處理,接下來(lái)說(shuō)說(shuō)如何繞過(guò)證書校驗(yàn)。
iOS繞過(guò)https證書校驗(yàn)
UIWebView
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (!_authenticated) {
_authenticated = YES;
_urlConnection = [[NSURLConnection alloc] initWithRequest:_requestW delegate:self];
[_urlConnection start];
return NO;
}
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge previousFailureCount] == 0){
_authenticated = YES;
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
} else{
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
_authenticated = YES;
[self loadRequest:_requestW];
[_urlConnection cancel];
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
但是下面代理方法已經(jīng)廢棄
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge API_DEPRECATED("Use -connection:willSendRequestForAuthenticationChallenge: instead.", macos(10.2,10.10), ios(2.0,8.0), watchos(2.0,2.0), tvos(9.0,9.0));
在這次定制中,就是因?yàn)闆](méi)有進(jìn)入下面兩代理方法,導(dǎo)致客戶那邊無(wú)法打開(kāi)app內(nèi)嵌到網(wǎng)頁(yè),所以要用下面的方法代替
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
SDWebView
這個(gè)問(wèn)題沒(méi)有解決,但是代碼中已經(jīng)設(shè)置了繞過(guò)證書校驗(yàn),
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
這個(gè)方法中options配置為SDWebImageAllowInvalidSSLCertificates(信任所有證書),在SDWebImageDownloader中也對(duì)證書校驗(yàn)進(jìn)行了處理,
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
// Identify the operation that runs this task and pass it the delegate method
NSOperation<SDWebImageDownloaderOperationInterface> *dataOperation = [self operationWithTask:task];
if ([dataOperation respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]) {
[dataOperation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
} else {
if (completionHandler) {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
}
}
真正的處理是在SDWebImageDownloaderOperation中進(jìn)行,跟webView的處理方式是一樣的。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if (!(self.options & SDWebImageDownloaderAllowInvalidSSLCertificates)) {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
} else {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
disposition = NSURLSessionAuthChallengeUseCredential;
}
} else {
if (challenge.previousFailureCount == 0) {
if (self.credential) {
credential = self.credential;
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
AFNetworking
AF是因?yàn)锳FImageDownloader中AFHTTPSessionManager沒(méi)有設(shè)置securityPolicy,只要在initWithSessionConfiguration:方法中添加sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];就可以繞過(guò)證書校驗(yàn)。
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
sessionManager.responseSerializer = [AFImageResponseSerializer serializer];
sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
return [self initWithSessionManager:sessionManager
downloadPrioritization:AFImageDownloadPrioritizationFIFO
maximumActiveDownloads:4
imageCache:[[AFAutoPurgingImageCache alloc] init]];
}
以上就是這次踩完坑后的收獲,有什么不對(duì)的地方還請(qǐng)路過(guò)的朋友們指點(diǎn)。