iOS开发app实现支持HTTPS

什么是HTTPS?

在说HTTPS之前先说说什么是HTTP,HTTP就是我们平时浏览网页时候使用的一种协议。HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全。为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。

如果从最终的数据解析角度去看HTTPS,HTTPS与HTTP没有任何区别,HTTPS就是将HTTP协议数据包放到SSL/TSL层加密后,在TCP/IP层组成IP数据报去传输,以此保证传输数据的安全;在接受端,SSL/TSL将接收的数据包解密之后,将数据传给HTTP协议层,获得普通的HTTP数据,简单地概括为 HTTPS = HTTP + SSL/TLS + TCP。

在WWDC 2016开发者大会上,苹果宣布在2016年底前所有iOS应用都必须启用App Transport Security安全功能,对于未启用App Transport Security安全功能的app苹果都会进行更严格的审核,如果没有充分的理由,很可能你的app会被拒绝上架。

此处引用苹果官方文档内容:

App Store Review for ATS
此处省略一大段苹果官方文档内容.....
When submitting your app to the App Store, provide sufficient information for the App Store to determine why your app cannot make secure connections by default.

一:公司使用的是知名CA颁发的证书,服务器配置符合苹果ATS要求:

App Transport Security 要求 TLS 1.2,而且它要求站点使用支持forward secrecy协议的密码。证书也要求是符合ATS规格的,ATS只信任知名CA颁发的证书,小公司所使用的 self signed certificate,还是会被ATS拦截。因此慎重检查与你的应用交互的服务器是不是符合ATS的要求非常重要。

官方文档App Transport Security Technote对CA颁发的证书要求:

The leaf server certificate must be signed with one of the following types of keys:
1.Rivest-Shamir-Adleman (RSA) key with a length of at least 2048 bits
2.Elliptic-Curve Cryptography (ECC) key with a size of at least 256 bits
In addition, the leaf server certificate hashing algorithm must be Secure Hash Algorithm 2 (SHA-2) with a digest length, sometimes called a “fingerprint,” of at least 256 (that is, SHA-256 or greater).

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

二:基于AFNetWorking的SSL特定服务器证书信任处理,重写AFNetWorking的customSecurityPolicy方法,这里我创建了一个HttpRequest工具类,分别对GET和POST方法进行了封装,以GET方法为例:
+ (void)getWithUrl:(NSString *)url
            params:(NSDictionary *)params
           success:(void (^)(id))success
              fail:(void (^)(NSError *))fail
{
 //初始化AFHTTPSessionManager 
    AFHTTPSessionManager *manager = [self manager];

// 发出GET请求
    [manager GET:url parameters:params success:^(AFHTTPRequestOperation *operation, id response){
        if (success) {
            success(response);
        }
    } fail:^(AFHTTPRequestOperation *operation, NSError *error) {
        if (error) {
            fail(error);
        }
    }];
}

+ (AFHTTPSessionManager *)manager {
  //初始化AFHTTPSessionManager 
   AFHTTPSessionManager  *manager = [AFHTTPSessionManager manager];
   switch (sg_requestType) {
        case kZHRequestTypeJSON: {
            manager.requestSerializer = [AFJSONRequestSerializer serializer];
            break;
        }
        case kZHRequestTypePlainText: {
            manager.requestSerializer = [AFHTTPRequestSerializer serializer];
            break;
        }
    }
    // https ssl 验证  KOpenHttpsSSL为证书名称的宏
    if (KOpenHttpsSSL) {
        [manager setSecurityPolicy:[self customSecurityPolicy]];
    }
    return manager;
}
+ (AFSecurityPolicy*)customSecurityPolicy {
    // /先导入证书
    NSString *cerFilePath = [[NSBundle mainBundle] pathForResource:KCertificateName ofType:@"cer"];//证书的路径
    NSData *certFileData = [NSData dataWithContentsOfFile:cerFilePath];
    
    // AFSSLPinningModeCertificate 证书验证模式
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    
    // allowInvalidCertificates 是否允许 self signed certificate
    // 验证 self signed certificate要设置为YES
    securityPolicy.allowInvalidCertificates = YES;
    //是否需要验证域名
    securityPolicy.validatesDomainName = NO;
    
    securityPolicy.pinnedCertificates = [NSSet setWithArray:@[certFileData]];
    
    return securityPolicy;
}

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

三:更改系统方法,发送异步NSURLConnection请求。
//发送https网络请求
- (void)sendHttpsRequest
{
    NSString *urlStr = @"https://api.weibo.com/";
    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);
    }
    
    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信任证书的问题得以解决。

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

推荐阅读更多精彩内容

  • 原文地址 http://blog.csdn.net/u012409247/article/details/4985...
    0fbf551ff6fb阅读 3,512评论 0 13
  • 一、作用 不使用SSL/TLS的HTTP通信,就是不加密的通信。所有信息明文传播,带来了三大风险。 (1)窃听风险...
    XLsn0w阅读 10,479评论 2 44
  • 一. HTTPS 其实HTTPS从最终的数据解析的角度,与HTTP没有任何的区别,HTTPS就是将HTTP协议数据...
    独酌丿红颜阅读 3,644评论 4 122
  • 天空灰白 像吸满水的棉 行人紧张的看着天 脚步匆匆 担心不知道哪一秒大雨点就会被风吹下来 终于到了目的地 放松的看...
    猫二蛋阅读 175评论 0 0
  • 世事怎能念曾经 今天一早看到这样一句话:命运嘛,休论公道! 短短七个字,平静之中有着点无奈、清醒、看透,但在这通透...
    狗西西阅读 863评论 0 0