iOS服务器证书不受信任的解决版本-程序员宅基地

参考文章链接:

https://www.cnblogs.com/v-jing/p/6008964.html

http://www.cocoachina.com/ios/20151021/13722.html

https://www.cnblogs.com/weak/p/6142508.html

http://blog.csdn.net/samuelandkevin/article/details/53392748

http://www.jianshu.com/p/7c89b8c5482a

比较通用的解决办法之一:

NSURLProtocol

MKNetworkOperation 可以设置 

request.shouldContinueWithInvalidCertificate = YES;

//采用NSURLConnection进行网络请求,会调用此方法
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

{

//证书处理
if([kBaseURL rangeOfString:@"youhost"].location != NSNotFound)

{

}
// 判断是否是信任服务器证书
if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
// 告诉服务器,客户端信任证书
// 创建凭据对象
NSURLCredential *credntial = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
// 告诉服务器信任证书
[challenge.sender useCredential:credntial forAuthenticationChallenge:challenge];
}

}

在AFN中如何绕过证书验证呢?设置AFSecurityPolicy参数:

//证书处理
//后台自建证书,因为证书无效导致AFN请求被取消,此段代码用在外网测试环境
if([kBaseURL rangeOfString:@"testapp.gtax.cn"].location != NSNotFound){
AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
securityPolicy.allowInvalidCertificates = YES;
securityPolicy.validatesDomainName = NO;
self.requestManager.securityPolicy = securityPolicy;
}

 

设置自定义证书的方法

引用方便学习,内容来源:http://www.jianshu.com/p/6b9c8bd5005a

 

发送HTTPS请求信任SSL证书和自签名证书,分为三种情况

 

1.如果你的app服务端安装的是SLL颁发的CA,可以使用系统方法直接实现信任SSL证书,关于Apple对SSL证书的要求请参考:苹果官方文档CertKeyTrustProgGuide

 

这种方式不需要在Bundle中引入CA文件,可以交给系统去判断服务器端的证书是不是SSL证书,验证过程也不需要我们去具体实现。
示例代码:

 

    NSURL *URL = [NSURL URLWithString:URLString];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10]; //创建同步连接 NSError *error = nil; NSData *receivedData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error]; NSString *receivedInfo = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];

 

当然,如果你需要同时信任SSL证书和自签名证书的话还是需要在代码中实现CA的验证,这种情况在后面会提到。

 

2.基于AFNetWorking的SSL特定服务器证书信任处理,重写AFNetWorking的customSecurityPolicy方法,这里我创建了一个HttpRequest类,分别对GET和POST方法进行了封装,以GET方法为例:

 

+ (void)get:(NSString *)url params:(NSDictionary *)params success:(void (^)(id))success failure:(void (^)(NSError *))failure 
{ // 1.获得请求管理者 AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager]; // 2.申明返回的结果是text/html类型 mgr.responseSerializer = [AFHTTPResponseSerializer serializer]; // 3.设置超时时间为10s mgr.requestSerializer.timeoutInterval = 10; // 加上这行代码,https ssl 验证。 if(openHttpsSSL) { [mgr setSecurityPolicy:[self customSecurityPolicy]]; } // 4.发送GET请求 [mgr GET:url parameters:params success:^(AFHTTPRequestOperation *operation, id responseObj){ if (success) { success(responseObj); } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if (error) { failure(error); } }]; }

 

+ (AFSecurityPolicy*)customSecurityPolicy 
{ // /先导入证书 NSString *cerPath = [[NSBundle mainBundle] pathForResource:certificate ofType:@"cer"];//证书的路径 NSData *certData = [NSData dataWithContentsOfFile:cerPath]; // AFSSLPinningModeCertificate 使用证书验证模式 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 = NO; securityPolicy.pinnedCertificates = @[certData]; return securityPolicy; }

 

其中的cerPath就是app bundle中证书路径,certificate为证书名称的宏,仅支持cer格式,securityPolicy的相关配置尤为重要,请仔细阅读customSecurityPolicy方法并根据实际情况设置其属性。

 

这样,就能够在AFNetWorking的基础上使用HTTPS协议访问特定服务器,但是不能信任根证书的CA文件,因此这种方式存在风险,读取pinnedCertificates中的证书数组的时候有可能失败,如果证书不符合,certData就会为nil。

 

3.更改系统方法,发送异步NSURLConnection请求。

 

- (void)getDataWithURLRequest {
    //connection
    NSString *urlStr = @"https://developer.apple.com/cn/";
    NSURL *url = [NSURL URLWithString:urlStr]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10]; NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self]; [connection start]; }

 

重点在于处理NSURLConnection的didReceiveAuthenticationChallenge代理方法,对CA文件进行验证,并建立信任连接。

 

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {

    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; } - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { /* //直接验证服务器是否被认证(serverTrust),这种方式直接忽略证书验证,直接建立连接,但不能过滤其它URL连接,可以理解为一种折衷的处理方式,实际上并不安全,因此不推荐。 SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust]; return [[challenge sender] useCredential: [NSURLCredential credentialForTrust: serverTrust] forAuthenticationChallenge: challenge]; */ if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]) { do { SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust]; NSCAssert(serverTrust != nil, @"serverTrust is nil"); if(nil == serverTrust) break; /* failed */ /** * 导入多张CA证书(Certification Authority,支持SSL证书以及自签名的CA) */ NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"cloudwin" ofType:@"cer"];//自签名证书 NSData* caCert = [NSData dataWithContentsOfFile:cerPath]; NSString *cerPath2 = [[NSBundle mainBundle] pathForResource:@"apple" ofType:@"cer"];//SSL证书 NSData * caCert2 = [NSData dataWithContentsOfFile:cerPath2]; NSCAssert(caCert != nil, @"caCert is nil"); if(nil == caCert) break; /* failed */ NSCAssert(caCert2 != nil, @"caCert2 is nil"); if (nil == caCert2) { break; } SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert); NSCAssert(caRef != nil, @"caRef is nil"); if(nil == caRef) break; /* failed */ SecCertificateRef caRef2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert2); NSCAssert(caRef2 != nil, @"caRef2 is nil"); if(nil == caRef2) break; /* failed */ NSArray *caArray = @[(__bridge id)(caRef),(__bridge id)(caRef2)]; NSCAssert(caArray != nil, @"caArray is nil"); if(nil == caArray) break; /* failed */ OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray); NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed"); if(!(errSecSuccess == status)) break; /* failed */ SecTrustResultType result = -1; status = SecTrustEvaluate(serverTrust, &result); if(!(errSecSuccess == status)) break; /* failed */ NSLog(@"stutas:%d",(int)status); NSLog(@"Result: %d", result); BOOL allowConnect = (result == kSecTrustResultUnspecified) || (result == kSecTrustResultProceed); if (allowConnect) { NSLog(@"success"); }else { NSLog(@"error"); } /* https://developer.apple.com/library/ios/technotes/tn2232/_index.html */ /* https://developer.apple.com/library/mac/qa/qa1360/_index.html */ /* kSecTrustResultUnspecified and kSecTrustResultProceed are success */ if(! allowConnect) { break; /* failed */ } #if 0 /* Treat kSecTrustResultConfirm and kSecTrustResultRecoverableTrustFailure as success */ /* since the user will likely tap-through to see the dancing bunnies */ if(result == kSecTrustResultDeny || result == kSecTrustResultFatalTrustFailure || result == kSecTrustResultOtherError) break; /* failed to trust cert (good in this case) */ #endif // The only good exit point return [[challenge sender] useCredential: [NSURLCredential credentialForTrust: serverTrust] forAuthenticationChallenge: challenge]; } while(0); } // Bad dog return [[challenge sender] cancelAuthenticationChallenge: challenge]; }

 

这里的关键在于result参数的值,根据官方文档的说明,判断(result == kSecTrustResultUnspecified) || (result == kSecTrustResultProceed)的值,若为1,则该网站的CA被app信任成功,可以建立数据连接,这意味着所有由该CA签发的各个服务器证书都被信任,而访问其它没有被信任的任何网站都会连接失败。该CA文件既可以是SLL也可以是自签名。

 

NSURLConnection的其它代理方法实现

 

#pragma mark -- connect的异步代理方法
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    NSLog(@"请求被响应"); _mData = [[NSMutableData alloc]init]; } -(void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data { NSLog(@"开始返回数据片段"); [_mData appendData:data]; } -(void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"链接完成"); //可以在此解析数据 NSString *receiveInfo = [NSJSONSerialization JSONObjectWithData:self.mData options:NSJSONReadingAllowFragments error:nil]; NSLog(@"received data:\\\\n%@",self.mData); NSLog(@"received info:\\\\n%@",receiveInfo); } //链接出错 -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{ NSLog(@"error - %@",error); }

 

至此,HTTPS信任证书的问题得以解决,这不仅是为了响应Apple强制性使用ATS的要求,也是为了实际生产环境安全性的考虑,HTTPS是未来的趋势,建议尽早支持。

 

如需参考Demo请移步在Github上的开源项目

 

转载于:https://www.cnblogs.com/yuxiaoyiyou/p/7059959.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/diankuini1374/article/details/102336881

智能推荐

【转帖】接口测试人员必备知识技能_问个技术性问题,测接口的时候,怎么查看用户的资料-程序员宅基地

文章浏览阅读69次。一、首先明白接口是什么软件接口是指程序中具体负责在不同模块之间传输或接受数据的并做处理的类或者函数。(而不是指传输的数据!!)二、什么是接口测试接口测试就是通过向接口传递数据来测试这个接口是否正确。比如:一个QQ登录功能接口,就需要我们传递QQ号和密码去验证这个登录接口是否正确,能否使用。三、进行接口测试需要掌握哪些知识1、了解系统及内部各个组件之间的业务逻辑交互;2、了解接口的I/O(input/output:输入输出);3、了解协议的基本内容,包括:通信原理、三次握手、常用的_问个技术性问题,测接口的时候,怎么查看用户的资料

跟我打卡LeetCode 58最后一个单词长度&59螺旋矩阵Ⅱ&60排列序列_螺旋矩阵 58-程序员宅基地

文章浏览阅读2.7k次。最后一个单词长度题目描述给定一个仅包含大小写字母和空格 ’ ’ 的字符串 s,返回其最后一个单词的长度。如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。如果不存在最后一个单词,请返回 0 。说明:一个单词是指仅由字母组成、不包含任何空格字符的 最大子字符串。示例:输入: “Hello World”输出: 5分析这题其实很简单,就是三个步骤模拟这个过程就可以了:具体代码:public int lengthOfLastWord(String s) { if(_螺旋矩阵 58

使用eclipse查看java源码报错source not found_jar has no source attachment-程序员宅基地

文章浏览阅读2.5k次,点赞7次,收藏5次。使用eclipse查看java源码报错source not found的解决办法问题:编写java代码时想要查看调用的系统的类或者接口时,使用快捷键(按住Ctrl,单击类或接口名)报错source not found现象:Source not foundThe JAR file D:\Java\lib\rt.jar has no source attachment.You can at..._jar has no source attachment

NVIDIA官网如何下载所有历史版本的驱动,包括上古化石版本?_nvdia驱动历史版本-程序员宅基地

文章浏览阅读608次,点赞9次,收藏9次。120.0.6099.199(正式版本) (64 位))以及系统之后,会发现出来的驱动最多只有。,然后再粘贴上面代码,然后回车并再点一次。下载历史版本的驱动,但是当你选择好你的。,而我们需要的是更老的版本,比如。出于种种可能的原因,我们需要在。都看到这里了,还不赶紧。如上图所示,最老的驱动为。那就按照他提示,手动输入。型号(比如我的上古显卡。_nvdia驱动历史版本

『踩坑记录』IDEA Spring initialzr新建Spring项目不能选择jdk8的解决方法_spring initaller 选择不了jdk8-程序员宅基地

文章浏览阅读1.6k次,点赞6次,收藏6次。Spring initializr新建Spring项目不能选低版本java。_spring initaller 选择不了jdk8

开发工具 之十 详解 RAM、ROM、FLASH、MMC、SD 等存储器_mmc mos-程序员宅基地

文章浏览阅读953次,点赞26次,收藏22次。存储器是电子系统中必不可少的存储设备,主要用于存放程序(指令)和数据。RAM(Random Access Memory)存储单元的内容可按照需要随机取出或存入,且存取的速度与存储单元的位置无关。这种存储器在断电时,将丢失其存储内容。ROM(Read Only Memory,只读存储器)是一种只能读出事先所存的数据的固态半导体存储器。ROM 中所存数据稳定,断电后所存数据也不会消失。_mmc mos

随便推点

《论文阅读》Video Super-resolution with Temporal Group Attention-程序员宅基地

文章浏览阅读1.7k次。留个笔记自用Video Super-resolution with Temporal Group Attention做什么Video super-resolution视频超分辨,经典应用,低分辨率的图片在放大后必然是模糊的,超分辨做的便是将低分辨的图片转化成高分辨率的图片并且做到清晰。这里的视频超分辨也是同样一个意思,不过将图片转换成了视频的某个部分,但这里有所增加的依靠便是视频存在着上下帧。做了什么怎么做总结..._video super-resolution with temporal group attention

操作系统置换策略基本算法_opt策略-程序员宅基地

文章浏览阅读3.2k次,点赞4次,收藏8次。页面置换:在地址映射过程中,若在页面中发现所要访问的页面不再内存中,则产生缺页中断(page fault)。当发生缺页中断时操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。 典型的置换算法有四种,如下所示: OPT:最佳替换算法(optional replacement)。替换下次访问距当前时间最长的页。opt算法需要知道操作_opt策略

web移动开发总结(一)_简述移动web开发的特点-程序员宅基地

文章浏览阅读1w次,点赞29次,收藏108次。概述移动web的概念和发展历史移动web的开发方式和区别响应式原理和媒体查询(重点)响应式框架的介绍bootstrap框架的基本使用bootstrap全局CSS样式布局容器使用(重点)bootstrap全局CSS样式栅格系统使用(重点)bootstrap全局CSS样式响应式工具使用微金所项目搭建和头部的结构 (重点)web前端PC端的web : 在PC端电脑访问的web..._简述移动web开发的特点

Radare2 框架介绍及使用-程序员宅基地

文章浏览阅读1.4k次。这是整个框架的核心工具,它具有debugger和Hexeditor的核心功能,使您能够像打开普通的文件一样,打开许多输入/输出源,包括磁盘、网络连接、内核驱动和处于调试中的进程等。它实现了一个高级的命令行界面,可用于在文件内部活动和浏览,分析数据,反编译,打补丁,比较数据,搜索,替换和可视化。您可以用多种编程语言编写radare2的脚本,包括Python, Ruby, JavaScript, Lua, 和 Perl。该程序用于从可执行文件中提取信息,例如ELF, PE, Java CLASS, Mach-O_radare2

安装RASP管理后台-程序员宅基地

文章浏览阅读687次,点赞2次,收藏2次。前提:安装java环境一、安装MongoDB1、创建仓库vi /etc/yum.repos.d/mongodb-org-3.6.repo2、把下面内容复制到文件中,保存退出[mongodb-org-3.6]name=MongoDB Repositorybaseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-or...

paypal Java 查询支付状态_paypal 接口开发 主动查询支付状态-程序员宅基地

文章浏览阅读718次。查询支付状态这里只讲查询支付状态的,获得 token 什么的比较简单,这里就不讲解了。这里是根据按钮进行对接的,也就是这篇根据回调参数 txn_id 查询支付状态官网有Show captured payment detailspostman只需要带个 token 就行了代码如下 public static ServiceResult<String> getPaypalPaymentResult(String txnId) { if (StringUtil_paypal 接口开发 主动查询支付状态