聊聊SSL证书

写这篇文章的目的

之前在做一个奇葩的定制,遇到提示“无法验证服务器的身份,可能链接到伪装服务器”的错误,然而我自己这边的demo没有出现这个问题,一度表示很无奈。还有一个是使用SDWebImage加载网络图片时,图片下载失败(图片地址也是https),还有一个是使用UIImageView+AFNetworking加载图片时,请求被cancel。跟客户沟通了很久,才解决了以上问题,所以想写篇文章记录一下。

什么是SSL

SSL证书是数字证书的一种,也称为SSL服务器证书,SSL证书通过在客户端浏览器和Web服务器建立一条SSL安全通道,实现数据信息在客户端和服务器之间的加密传输,可以防止数据信息的泄露,保证了双方传递信息的安全性,而且用户可以通过服务器证书验证他所访问的网站是否是真实可靠。

苹果ATS对SSL做了以下要求:
  • 使用SHA2级别的证书签名算法,例如SHA-256, SHA-512等;
  • 证书公钥算法使用RSA 2048位及以上,或使用更高的算法ECC 256加密算法;

从权威机构认证过的证书一般都符合苹果ATS要求,自建证书基本都需要APP去绕过证书校验的步骤。符合要求的很好处理,接下来说说如何绕过证书校验。

iOS绕过https证书校验

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];
}

但是下面代理方法已经废弃

- (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));

在这次定制中,就是因为没有进入下面两代理方法,导致客户那边无法打开app内嵌到网页,所以要用下面的方法代替

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

SDWebView

这个问题没有解决,但是代码中已经设置了绕过证书校验,

- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options

这个方法中options配置为SDWebImageAllowInvalidSSLCertificates(信任所有证书),在SDWebImageDownloader中也对证书校验进行了处理,

- (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 *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中进行,跟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是因为AFImageDownloader中AFHTTPSessionManager没有设置securityPolicy,只要在initWithSessionConfiguration:方法中添加sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];就可以绕过证书校验。

- (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]];
}

以上就是这次踩完坑后的收获,有什么不对的地方还请路过的朋友们指点。

你可能感兴趣的:(聊聊SSL证书)