iOS-移动架构师iOSiOS开发·网络篇

iOS开发之HTTPS

2016-09-18  本文已影响615人  even_cheng

在今年的WWDC 2016苹果开发者大会上,苹果再一次提到了数据安全方面,并且宣布iOS应用将从2017年1月起启用名为ATS的安全传输功能,将强制使用HTTPS安全连接。

首先,ATS 安全标准是苹果在发布 iOS 9 和 OS X EI Capitan 系统时发布的,这一标准通过强行推动一系列安全实际操作,从而积极促进安全性,同时还要求网络请求必须在一个安全的链接上传输,当开启 ATS 之后,网络传输将自动通过 HTTPS 协议传输而不是 HTTP 协议。启用HTTPS网络连接之后,数据传输的安全性将大幅提升,不容易被黑客拦截破译。

那么HTTPS相对于HTTP到底有何区别和优势呢?互联网全站HTTPS的时代已经到来,这篇文章其实已经给我们做了比较清楚的对比和分析,如果关于两者的基础知识还不太理解的,我建议可以阅读一些相关资料,比如马海祥博客HTTP与HTTPS的区别也对两者做了基本分析,值得一读。

这里我想给大家分享的不是基础知识点,也不是深入剖析两者的优劣,因为已经有很多资历深厚的前辈给我们做了大量的教程,只要你不懒,我相信是可以找到更多学习资料的。

所以今天我们还是从iOS开发入手,就聊一下关于HTTP和HTTPS到底该怎么使用,两者的实际开发流程有什么区别。

做iOS开发的对下面这段代码应该比较熟悉了:

Snip20160918_1.png

自从苹果开始大力推崇HTTPS之后,我们使用HTTP连接就需要在info.plist进行上面的配置,不然就会出现下面这种报错:

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

这是因为xcode7之后,苹果要求APP使用HTTPS协议,修改方法是要么使服务器支持https访问,要么设置允许不安全的网络协议。我们常用的还是直接添加设置允许,所以就用到了上面的方法。那么问题来了,从2017年开始,苹果将强制全面采用HTTPS连接,目前国内能全站支持HTTPS的网络公司基本没有,或者少之又少,但是对于这样的发展趋势,我们不得不重视。对于开发者来说,开发安全的APP更是体现我们对用户负责的态度。

官方文档对HTTPS验证也有详细的说明。这里我们就说一下使用NSURLConnection和NSURLSession建立HTTPS连接以及AFNetworking对HTTPS的支持及使用方法。

一、NSURLConnection
首先,创建连接对象

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event

{

//1,URL

     NSURL* url = [NSURL URLWithString:@"https://m.baidu.com"];

//2,请求

     NSURLRequest* request = [NSURLRequest requestWithURL:url];

//3,链接

     NSURLConnection* connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];

//开始

    [connection start];

}

然后设置代理方法,进行进行连接请求
//#pragma mark- NSURLConnectionDataDelegate
//收到安全警告时候调用

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

{

//验证方式一:

//1.判断是否信任服务器

     if ([challenge.protectionSpace.authenticationMethod    isEqualToString:NSURLAuthenticationMethodServerTrust]) {

//2.获取到受保护空间里面的证书

     NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

//3.告诉服务器我信任你,我们建立安全通道

      [challenge.sender useCredential:credential   forAuthenticationChallenge:challenge];

      } else {

//5)验证失败,取消这次验证流程

      [challenge.sender cancelAuthenticationChallenge:challenge];

 }

//验证方式二:

//1)获取trust object

 SecTrustRef trust = challenge.protectionSpace.serverTrust;

 SecTrustResultType result;

//2)SecTrustEvaluate对trust进行验证

 OSStatus status = SecTrustEvaluate(trust, &result);

 if (status == errSecSuccess &&

 (result == kSecTrustResultProceed ||   

 result == kSecTrustResultUnspecified)) {

//3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接

 NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];

 [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];

 } else {

//5)验证失败,取消这次验证流程

 [challenge.sender cancelAuthenticationChallenge:challenge];

}

}

最后处理连接数据,监控连接过程:

//初始化

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response

{

     self.dataM = [NSMutableData data];

}

//接收数据

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

{

     [self.dataM appendData:data];

}

//错误信息

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error

{

     NSLog(@"error---%@",[error localizedDescription]);

}

//输出结果

- (void)connectionDidFinishLoading:(NSURLConnection *)connection

{

     NSLog(@"%@",[[NSString alloc]initWithData:self.dataM encoding:NSUTF8StringEncoding]);

}

上面是代码是通过系统默认验证流程来验证证书的。假如我们是自建证书的呢?这样Trust Object里面服务器的证书因为不是可信任的CA签发的,所以直接使用SecTrustEvaluate进行验证是不会成功。又或者,即使服务器返回的证书是信任CA签发的,又如何确定这证书就是我们想要的特定证书?这就需要先在本地导入证书,设置成需要参与验证的Anchor Certificate(锚点证书,通过SecTrustSetAnchorCertificates设置了参与校验锚点证书之后,假如验证的数字证书是这个锚点证书的子节点,即验证的数字证书是由锚点证书对应CA或子CA签发的,或是该证书本身,则信任该证书),再调用SecTrustEvaluate来验证。代码如下

//先导入证书

 NSString * cerPath = ...; //证书的路径

 NSData * cerData = [NSData dataWithContentsOfFile:cerPath];

 SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));

 self.trustedCertificates = @[CFBridgingRelease(certificate)];

//回调

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

//1)获取trust object

     SecTrustRef trust = challenge.protectionSpace.serverTrust;  

     SecTrustResultType result;

//注意:这里将之前导入的证书设置成下面验证的Trust Object的anchor certificate

     SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)self.trustedCertificates);

//2)SecTrustEvaluate会查找前面SecTrustSetAnchorCertificates设置的证书或者系统默认提供的证书,对trust进行验证

     OSStatus status = SecTrustEvaluate(trust, &result);

     if (status == errSecSuccess &&

     (result == kSecTrustResultProceed ||

     result == kSecTrustResultUnspecified)) {

//3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接

     NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];

     [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];

     } else {

//5)验证失败,取消这次验证流程

     [challenge.sender cancelAuthenticationChallenge:challenge];

     }

}

二、NSURLSession
首先创建session对象并进行懒加载

-(NSURLSession *)session
{
     if (!_session)
     {
     NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];

     _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];

     }

     return _session;

}

然后创建连接对象

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event

{

//1,URL

     NSURL* url = [NSURL URLWithString:@"https://m.baidu.com"];

//2,链接

     [[self.session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

     if (!error && data.length > 0)

     {

     NSString* result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];

     NSLog(@"%@",result);

     }

     else

     {

     NSLog(@"error---%@",[error localizedDescription]);

     }

     }] resume];

}

最后监控连接,进行判断

#pragma mark - NSURLSessionDelegate

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task

didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge

completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler

{

//1.判断是否信任服务器

     if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

//2.获取到受保护空间里面的证书

     NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

//3.告诉服务器我信任你,我们建立安全通道

     [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];

     }

}

三、AFNetworking配置

NSURL * url = [NSURL URLWithString:@"https://m.baidu.com"];

AFHTTPRequestOperationManager * requestOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:url];

dispatch_queue_t requestQueue = dispatch_create_serial_queue_for_name("kRequestCompletionQueue");

requestOperationManager.completionQueue = requestQueue;

AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

//allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO

//如果是需要验证自建证书,需要设置为YES

securityPolicy.allowInvalidCertificates = YES;

//validatesDomainName 是否需要验证域名,默认为YES;

//假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。

//置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。

//如置为NO,建议自己添加对应域名的校验逻辑。

securityPolicy.validatesDomainName = YES;

//validatesCertificateChain 是否验证整个证书链,默认为YES

//设置为YES,会将服务器返回的Trust Object上的证书链与本地导入的证书进行对比,这就意味着,假如你的证书链是这样的:

//GeoTrust Global CA

//    Google Internet Authority G2

//        *.google.com

//那么,除了导入*.google.com之外,还需要导入证书链上所有的CA证书(GeoTrust Global CA, Google Internet Authority G2);

//如是自建证书的时候,可以设置为YES,增强安全性;假如是信任的CA所签发的证书,则建议关闭该验证,因为整个证书链一一比对是完全没有必要(请查看源代码);

securityPolicy.validatesCertificateChain = NO;

requestOperationManager.securityPolicy = securityPolicy;

对于更多详细的了解和使用可以建议大家参考一下Jamin's blog,感谢前辈的分享,让开发更高效。

上一篇 下一篇

猜你喜欢

热点阅读