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

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

第1种

#import "ViewController.h"

#import "HttpManager.h"

@interface ViewController ()<NSURLSessionDataDelegate>

@property(nonatomic,copy)NSString *etag;

@end

@implementation ViewController

- (void)viewDidLoad {

[super viewDidLoad];

UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 200, 100)];

[btn setTitle:@"AFNetworking" forState:UIControlStateNormal];

[btn setBackgroundColor:[UIColor redColor]];

[btn addTarget:self action:@selector(btnClicked) forControlEvents:UIControlEventTouchUpInside];

[self.view addSubview:btn];

UIButton *sessionBtn = [[UIButton alloc]initWithFrame:CGRectMake(100, 300, 200, 100)];

[sessionBtn setTitle:@"NSUrlSession" forState:UIControlStateNormal];

[sessionBtn setBackgroundColor:[UIColor redColor]];

[sessionBtn addTarget:self action:@selector(sessionBtnClicked) forControlEvents:UIControlEventTouchUpInside];

[self.view addSubview:sessionBtn];

}

- (void)btnClicked {

NSString *urlString = @"https://10.20.129.25:8443/dreamVideo/restful/show";

HttpManager *httpManager = [HttpManager shareHttpManager];

[httpManager post:urlString withParameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {

NSLog(@"success");

} failure:^(NSURLSessionDataTask *task, NSError *error) {

NSLog(@"failure");

}];

}

- (void)sessionBtnClicked {

NSString *urlString = @"https://10.20.129.25:8443/dreamVideo/restful/show";

NSURL *url = [NSURL URLWithString:urlString];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0f];

NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

NSURLSessionDataTask *task = [session dataTaskWithRequest:request];

[task resume];

}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask

didReceiveResponse:(NSURLResponse *)response

completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {

NSLog(@"接收到服务器响应");

//注意:这里需要使用completionHandler回调告诉系统应该如何处理服务器返回的数据

//默认是取消

/**

NSURLSessionResponseCancel = 0,            默认的处理方式,取消

NSURLSessionResponseAllow = 1,             接收服务器返回的数据

NSURLSessionResponseBecomeDownload = 2,    变成一个下载请求

NSURLSessionResponseBecomeStream           变成一个流

*/

completionHandler(NSURLSessionResponseAllow);

}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask

didReceiveData:(NSData *)data {

NSLog(@"获取到服务段数据");

NSLog(@"%@",[self jsonToDictionary:data]);

}

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

didCompleteWithError:(nullable NSError *)error {

NSLog(@"请求完成");

}

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge

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

NSLog(@"证书认证");

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:@"ca" ofType:@"cer"];//自签名证书

NSData* caCert = [NSData dataWithContentsOfFile:cerPath];

NSCAssert(caCert != nil, @"caCert is nil");

if(nil == caCert)

break; /* failed */

SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);

NSCAssert(caRef != nil, @"caRef is nil");

if(nil == caRef)

break; /* failed */

//可以添加多张证书

NSArray *caArray = @[(__bridge id)(caRef)];

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

}

/* 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

NSLog(@"信任该证书");

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

completionHandler(NSURLSessionAuthChallengeUseCredential,credential);

return [[challenge sender] useCredential: credential

forAuthenticationChallenge: challenge];

}

while(0);

}

// Bad dog

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

completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,credential);

return [[challenge sender] cancelAuthenticationChallenge: challenge];

}

注:调用SecTrustSetAnchorCertificates设置可信任证书列表后就只会在设置的列表中进行验证,会屏蔽掉系统原本的信任列表,要使系统的继续起作用只要调用SecTrustSetAnchorCertificates方法,第二个参数设置成NO即可。

AFNetworking中应该是这句(个人见解望指正)

SecTrustSetAnchorCertificatesOnly(serverTrust,NO);

第2种

#import "HttpManager.h"

#import "AFHTTPSessionManager.h"

@interface HttpManager()

@property(nonatomic,retain)AFHTTPSessionManager *manager;

@end

@implementation HttpManager

+(instancetype)shareHttpManager{

static dispatch_once_t onece = 0;

static HttpManager *httpManager = nil;

dispatch_once(&onece, ^(void){

httpManager = [[self alloc]init];

});

return httpManager;

}

//https访问

-(void)post:(NSString *)url withParameters:(id)parameters success:(void (^)(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject))success failure:(void (^)(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error))failure {

FSecurityPolicy分三种验证模式:
1、AFSSLPinningModeNone:只验证证书是否在新人列表中
2、AFSSLPinningModeCertificate:验证证书是否在信任列表中,然后再对比服务端证书和客户端证书是否一致
3、 AFSSLPinningModePublicKey:只验证服务端与客户端证书的公钥是否一致
这里我们选第二种模式,并且对AFSecurityPolicy的allowInvalidCertificates和 validatesDomainName进行设置。

AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

//    securityPolicy = [AFSecurityPolicy defaultPolicy];

//allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
//如果是需要验证自建证书,需要设置为YES

securityPolicy.allowInvalidCertificates = YES;

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

securityPolicy.validatesDomainName = NO;

//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;

_manager = [AFHTTPSessionManager manager];

_manager.responseSerializer = [AFHTTPResponseSerializer serializer];

_manager.securityPolicy = securityPolicy;

//设置超时时间

[_manager.requestSerializer willChangeValueForKey:@"timeoutinterval"];

_manager.requestSerializer.timeoutInterval = 20.f;

[_manager.requestSerializer didChangeValueForKey:@"timeoutinterval"];

_manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringCacheData;

//    if (_etag) {

//        [_manager.requestSerializer setValue:_etag forHTTPHeaderField:@"If-None-Match"];

//    } else {

//        [_manager.requestSerializer setValue:@"bb" forHTTPHeaderField:@"If-None-Match"];

//    }

_manager.responseSerializer.acceptableContentTypes  = [NSSet setWithObjects:@"application/xml",@"text/xml",@"text/plain",@"application/json",nil];

__weak typeof(self) weakSelf = self;

[_manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential) {

SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];

/**

*  导入多张CA证书(Certification Authority,支持SSL证书以及自签名的CA),请替换掉你的证书名称

*/

NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"ca" ofType:@"cer"];//自签名证书

NSData* caCert = [NSData dataWithContentsOfFile:cerPath];

NSArray *cerArray = @[caCert];

weakSelf.manager.securityPolicy.pinnedCertificates = cerArray;

SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);

NSCAssert(caRef != nil, @"caRef is nil");

NSArray *caArray = @[(__bridge id)(caRef)];

NSCAssert(caArray != nil, @"caArray is nil");

OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);

SecTrustSetAnchorCertificatesOnly(serverTrust,NO);

NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed");

 //选择质询认证的处理方式

NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;

__autoreleasing NSURLCredential *credential = nil;

 //NSURLAuthenticationMethodServerTrust质询认证方式

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

//基于客户端的安全策略来决定是否信任该服务器,不信任则不响应质询 。

if ([weakSelf.manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {

 //创建质询证书

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

  //确认质询方式

if (credential) {

disposition = NSURLSessionAuthChallengeUseCredential;

} else {

disposition = NSURLSessionAuthChallengePerformDefaultHandling;

}

} else {

//取消质询

disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;

}

} else {

disposition = NSURLSessionAuthChallengePerformDefaultHandling;

}

return disposition;

}];

[_manager POST:url parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {

NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;

NSDictionary *headDic = response.allHeaderFields;

NSInteger code = response.statusCode;

NSLog(@"response statusCode is %zd",code);

//        NSString *etag = headDic[@"Etag"];

//        if (etag) {

//            _etag = etag;

//        }

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

NSDictionary *responseDic = [self jsonToDictionary:[[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding]];

success(task,responseObject);

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;

NSDictionary *headDic = response.allHeaderFields;

NSInteger code = response.statusCode;

NSLog(@"response statusCode is %zd",code);

failure(task,error);

}];

}

- (NSDictionary *)jsonToDictionary:(NSString *)jsonString {

NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];

NSError *jsonError;

NSDictionary *resultDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:&jsonError];

return resultDic;

}

第3种

1). 第一步,先获取需要验证的信任对象(Trust Object)。这个Trust Object在不同的应用场景下获取的方式都不一样,对于NSURLConnection来说,是从delegate方法-connection:willSendRequestForAuthenticationChallenge:回调回来的参数challenge中获取([challenge.protectionSpace serverTrust])。

2). 使用系统默认验证方式验证Trust Object。SecTrustEvaluate会根据Trust Object的验证策略,一级一级往上,验证证书链上每一级数字签名的有效性(上一部分有讲解),从而评估证书的有效性。

3). 如第二步验证通过了,一般的安全要求下,就可以直接验证通过,进入到下一步:使用Trust Object生成一份凭证([NSURLCredential credentialForTrust:serverTrust]),传入challenge的sender中([challenge.sender useCredential:cred forAuthenticationChallenge:challenge])处理,建立连接。

4). 假如有更强的安全要求,可以继续对Trust Object进行更严格的验证。常用的方式是在本地导入证书,验证Trust Object与导入的证书是否匹配。更多的方法可以查看Enforcing Stricter Server Trust Evaluation,这一部分在讲解AFNetworking源码中会讲解到。

5). 假如验证失败,取消此次Challenge-Response Authentication验证流程,拒绝连接请求。

ps: 假如是自建证书的,则会跳过第二步,使用第三部进行验证,因为自建证书的根CA的数字签名未在操作系统的信任列表中

使用NSURLConnection支持HTTPS的实现

// Now start the connection
NSURL * httpsURL = [NSURL URLWithString:@"https://www.google.com"];
self.connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:httpsURL] delegate:self];
     
//回调
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)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];
         
  }
}
上面是代码是通过系统默认验证流程来验证证书的。假如我们是自建证书的呢?这样Trust Object里面服务器的证书因为不是可信任的CA签发的,所以直接使用SecTrustEvaluate进行验证是不会成功。又或者,即使服务器返回的证书是信任CA签发的,又如何确定这证书就是我们想要的特定证书?这就需要先在本地导入证书,设置成需要验证的Anchor Certificate(就是根证书),再调用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];
         
  }
}建议采用本地导入证书的方式验证证书,来保证足够的安全性。

iOS 的三种自建证书方法https请求相关配置的更多相关文章

  1. iOS用三种途径实现一方法有多个返回值

    以前觉得这种标题有点偏向于理论,实际开发中怎么会有这种诡异的需求,但是真正遇到了这种硬需求时觉得还是有那么点价值的,理论付诸了实践在此也就做了个整理. 以我私下开发中的一处代码为例,本意是希望有这么一 ...

  2. iOS 三种收起键盘的方法

    - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...

  3. 【读书笔记】iOS-开发技巧-三种收起键盘的方法

    - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...

  4. iOS的三种多线程技术NSThread/NSOperation/GCD

    1.iOS的三种多线程技术 1.NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程) 2.以下两点是苹果专门开发的"并发"技术,使得程序员可以不再去关心 ...

  5. File类三种得到路径的方法

    转: File类三种得到路径的方法 2010年11月29日 20:37:00 ssyan 阅读数:27123 标签: filemicrosoftstringexceptionwindowsunix   ...

  6. 【转】python 三种遍历list的方法

    [转]python 三种遍历list的方法 #!/usr/bin/env python # -*- coding: utf-8 -*- if __name__ == '__main__': list ...

  7. 【Java 线程的深入研究1】Java 提供了三种创建线程的方法

    Java 提供了三种创建线程的方法: 通过实现 Runnable 接口: 通过继承 Thread 类本身: 通过 Callable 和 Future 创建线程. 1.通过实现 Runnable 接口来 ...

  8. Oracle数据库三种标准的备份方法

    Oracle数据库的三种标准的备份方法: 1.导出/导入(EXP/IMP). 2.热备份. 3.冷备份. 注释:导出备件是一种逻辑备份,冷备份和热备份是物理备份. 一.导出/导入(Export/Imp ...

  9. iOS 用自签名证书实现 HTTPS 请求的原理

    在16年的WWDC中,Apple已表示将从2017年1月1日起,所有新提交的App必须强制性应用HTTPS协议来进行网络请求.默认情况下非HTTPS的网络访问是禁止的并且不能再通过简单粗暴的向Info ...

随机推荐

  1. 要想提高PHP的编程效率,你必须遵守的原则

    用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”(译注:PHP手册中说 ...

  2. Flask architecture

    论文The Flask Security Architecture: System Support for Diverse Security Policies 介绍了Flask architectur ...

  3. Office2003/2010等集成SP的简单方法

    Office2003集成SP的简单方法 需要准备的工具:Office 2003 光盘镜像.SP3更新包.Office 2003 序列号.UltraISO,7-zip或winrar,虚拟光驱 步骤一:提 ...

  4. math ceil以及math floor,math:round

    ◎Math.ceil()执行向上舍入,即它总是将数值向上舍入为最接近的整数:◎Math.floor()执行向下舍入,即它总是将数值向下舍入为最接近的整数:◎Math.round()执行标准舍入,即它总 ...

  5. HDU 1010 Temper of the bone(深搜+剪枝)

    Tempter of the Bone Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) ...

  6. PHP中使用正则表达式详解 preg_match() preg_replace() preg_mat

    PHP中嵌入正则表达式常用的函数有四个: 1.preg_match() :preg_match() 函数用于进行正则表达式匹配,成功返回 1 ,否则返回 0 . 语法:int preg_match( ...

  7. MySQL 5.7 for Windows 解压缩版配置安装

    从MYSQL5.7.6开始,安装MYSQL提示“请键入 NET HELPMSG 3534 以获得更多的帮助”的解决办法 今天安装MySQL提示如下错误: ----------------------- ...

  8. 【使用Itext处理PDF文档(新建PDF文件、修改PDF文件、PDF中插入图片、将PDF文件转换为图片)】

    iText简介 iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库.通过iText不仅可以生成PDF或rtf的文档,而且可以将XML.Html文件转 ...

  9. What is “Mock You” :Raise,callback,verify [转载]

    http://www.cnblogs.com/wJiang/archive/2010/02/21/1670637.html Raise 如果你说会用Setup,那么Raise就更简单了.这里注意下它是 ...

  10. 剑指offer 整数中1 出现的次数

    给定一个十进制正整数N,写下从1开始,到N的所有整数,然后数一下其中出现的所有"1"的个数. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1 ...