欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS之Https自签名证书认证及数据请求的封装原理

程序员文章站 2024-02-15 20:12:58
摘要: 在wwdc 2016开发者大会上,苹果宣布了一个最后期限:到2017年1月1日 app store中的所有应用都必须启用 app transport securit...

摘要: 在wwdc 2016开发者大会上,苹果宣布了一个最后期限:到2017年1月1日 app store中的所有应用都必须启用 app transport security安全功能。app transport security(ats)是苹果在ios 9中引入的一项隐私保护功能,屏蔽明文http资源加载,连接必须经过更安全的https。苹果目前允许开发者暂时关闭ats,可以继续使用http连接,但到年底所有官方商店的应用都必须强制性使用ats。

项目中使用的框架是afnetworking 3.0及以上版本,由于ats的原因,ios只允许使用https开头的链接,在2016年12月30日以前苹果允许绕开ats,如下图所示:

iOS之Https自签名证书认证及数据请求的封装原理

但是从2017年1月1日开始将不再接受使用http加载资源的应用,因此本篇文章主要讲解如何使用afn进行自签名证书的通过认证(注:对于使用ca机构认证的证书不需要进行认证,直接使用https开头的链接进行数据访问和加载页面即可)项目已经上传至github(需要参考源码的话请点击链接):httpssignaturecertificate_jb51.rar

1,建立一个根类 此处命名为aknetpackegeafn

 1>  .h文件 ,创建所需要的get 与 post 方法

#import <foundation/foundation.h>


typedef enum{
  
  aknetworkget ,  /**< get请求 */
  aknetworkpost = 1 /**< post请求 */
}aknetworktype;
typedef void (^httpsuccess)(id json);
typedef void (^httperro)(nserror* error);
@interface aknetpackegeafn : nsobject

+(instancetype)sharehttpmanager;

/*
 *
 networktype:请求方式 get 或 post
 signature:是否使用签名证书,是的话直接写入证书名字,否的话填nil
 api:请求的url接口
 parameters:请求参数
 sucess:请求成功时的返回值
 fail:请求失败时的返回值
 *
 */

- (void)networktype:(aknetworktype)networktype signature:(nsstring *)signature api:(nsstring *)api parameters:(nsdictionary *)parameters success:(httpsuccess)sucess fail:(httperro)fail;

@end

2> .m文件,导入头文件afnetworking.h 新建manager 属性并实现sharehttpmanager类方法

#import "aknetpackegeafn.h"
#import "afnetworking.h"

@interface aknetpackegeafn()

@property (nonatomic,strong) afhttpsessionmanager *manager;

@end

@implementation aknetpackegeafn


+(instancetype)sharehttpmanager{
  static dispatch_once_t onece = 0;
  static aknetpackegeafn *httpmanager = nil;
  dispatch_once(&onece, ^(void){
    httpmanager = [[self alloc]init];
  });
  return httpmanager;
}

2,get 与post 方法的实现

使用时将后台所给的证书转换为 .cer格式 拖入项目根目录中,在方法中进行绑定即可例如后台给的证书名为:kuture.crt  收到证书后双击进行安装,然后打开钥匙串,将名为kuture的证书右击导出,选择后缀为.cer 然后确定即可 如下图所示:

iOS之Https自签名证书认证及数据请求的封装原理  -->   iOS之Https自签名证书认证及数据请求的封装原理  -->

iOS之Https自签名证书认证及数据请求的封装原理 --> iOS之Https自签名证书认证及数据请求的封装原理

get 与 post 实现方法的封装

- (void)networktype:(aknetworktype)networktype signature:(nsstring *)signature api:(nsstring *)api parameters:(nsdictionary *)parameters success:(httpsuccess)sucess fail:(httperro)fail{
  
  //开启证书验证模式
  afsecuritypolicy *securitypolicy = [afsecuritypolicy policywithpinningmode:afsslpinningmodecertificate];
  
  //是否允许使用自签名证书
  signature == nil ? (void)(securitypolicy.allowinvalidcertificates = no):(securitypolicy.allowinvalidcertificates = yes);
  
  //是否需要验证域名
  securitypolicy.validatesdomainname = no;
  
  _manager = [[afhttpsessionmanager alloc]initwithbaseurl:[nsurl urlwithstring:api]];
  _manager.responseserializer = [afjsonresponseserializer serializer];
  _manager.securitypolicy = securitypolicy;
  _manager.responseserializer.acceptablecontenttypes = [nsset setwithobjects:@"application/json",@"application/xml",@"text/xml",@"text/json",@"text/plain",@"text/javascript",@"text/html", nil];
  
  if (signature != nil){
    
    __weak typeof(self) weakself = self;
    [_manager setsessiondidreceiveauthenticationchallengeblock:^nsurlsessionauthchallengedisposition(nsurlsession *session, nsurlauthenticationchallenge *challenge, nsurlcredential *__autoreleasing *_credential) {
      
      //获取服务器的 trust object
      sectrustref servertrust = [[challenge protectionspace] servertrust];
      
      //导入自签名证书
      nsstring *cerpath = [[nsbundle mainbundle] pathforresource:@"你的证书名字" oftype:@"cer"];
      nsdata *cerdata = [nsdata datawithcontentsoffile:cerpath];
      
      if (!cerdata) {
        
        nslog(@"==== .cer file is nil ====");
        
        return 0;
      }
      
      nsarray *cerarray = @[cerdata];
      weakself.manager.securitypolicy.pinnedcertificates = cerarray;
      seccertificateref caref = seccertificatecreatewithdata(null, (__bridge cfdataref)cerdata);
      nscassert(caref != nil, @"caref is nil");
      
      nsarray *caarray = @[(__bridge id)(caref)];
      nscassert(caarray != nil, @"caarray is nil");
      
      //将读取到的证书设置为servertrust的根证书
      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;
    }];
  }
  
  if (networktype == 0){
    
    [_manager get:api parameters:parameters progress:^(nsprogress * _nonnull uploadprogress) {
    } success:^(nsurlsessiondatatask * _nonnull task, id _nullable responseobject) {
      
      if (sucess){
        
        sucess(responseobject);
      }else{
        
        nslog(@"链接异常或网络不存在");
      }
    } failure:^(nsurlsessiondatatask * _nullable task, nserror * _nonnull error) {
      
      fail(error);
    }];

  }else if (networktype == 1){
    
    
    [_manager post:api parameters:parameters progress:^(nsprogress * _nonnull uploadprogress) {
    } success:^(nsurlsessiondatatask * _nonnull task, id _nullable responseobject) {
      
      if (sucess){
        
        sucess(responseobject);
      }else{
        
        nslog(@"链接异常或网络不存在");
      }
    } failure:^(nsurlsessiondatatask * _nullable task, nserror * _nonnull error) {
      
      fail(error);
    }];

  }  
}

2  使用方法,在需要进行数据获取或传递的类里面,直接导入头文件 aknetpackegeafn.h ,并实现方法即可,如下所示:

//创建对象
  //如果是自签名证书,使用前先到aknetpackegeafn相应的方法里进行证书的绑定(证书直接拖入项目中)即可
  /*
   *
   networktype:请求方式 get 或 post
   signature:是否使用签名证书,是的话直接写入证书名字,否的话填nil
   api:请求的url接口
   parameters:请求参数
   sucess:请求成功时的返回值
   fail:请求失败时的返回值
   *
   */
  
  aknetpackegeafn *nethttps = [aknetpackegeafn sharehttpmanager];
  [nethttps networktype:请求类型 signature:证书名称 api:请求url parameters:参数 success:^(id json) {
    
    nslog(@"json:%@",json);
  } fail:^(nserror *error) {
    
    nslog(@"error:%@",error);
  }];

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。