iOS开发之HTTPS

在今年的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,感谢前辈的分享,让开发更高效。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容