在项目中进行HTTPS适配

前言
    如今,信息安全越来越成为互联网的一个热议的话题,互联网用户也越来越重视保护网络中的个人隐私和个人信息。软件的良好的安全机制让运营者和使用者都受益。因此,信息安全成为了开发者与网络中不法分子的一场博弈。


1.http与https

    http与https 都是Web浏览器与服务器交互的数据传输协议。通过http进行传输的数据都是明文,没有进行任何的加密,如果传输的数据中包含银行卡号、密码等敏感信息,而且都没有进行加密,那么极易被不法份子或者黑客通过数据拦截而获取,这样就存在很大的安全隐患。
    为了解决http的这一缺陷,推出了https(安全套接字层超文本传输协议)。https是在http的基础上增加了SSL协议,客户端Web浏览器与服务器之间传递的所有数据都会被加密的。

2.https工作流程

    客户端在使用https方式与Web服务器通信时有以下几个步骤,如图所示。


2012071410212142.gif

    (1)客户使用https协议的URL发起Web服务器请求,要求与Web服务器建立SSL连接。
    (2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
    (3)客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
    (4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
    (5)Web服务器利用自己的私钥解密出会话密钥。
    (6)Web服务器利用会话密钥加密与客户端之间的通信。

3.项目适配

3.1.Web请求调整(NSURLRequest、NSMutableURLRequest)

    https请求有两种类型:域名和ip。
    举例如下:
    https://zone.qq.com/login.aspx?user=xx&password=xx
    https://192.168.67.35/login.aspx?user=xx&password=xx
    我们知道互联网中通过http协议进行数据传输都是根据双发的ip进行标记唯一对象的。而使用域名只是为了用户方便记忆网络地址,通信双方在进行数据交换之前都会将域名地址通过DNS(Domain Name System,域名系统)中的域名解析协议进行ip映射。因此,同一网站,不管你输入域名地址和ip地址,正常情况下都是可以访问的。
    iOS开发中,进行http和https数据请求的对象是NSURLRequest和NSMutableURLRequest,在发起数据请求之前,需要配置其host值域,该值为地址访问对应的证书中的域名。
    如上例中所示,host = zone.qq.com。

3.2.SSL验证逻辑调整

    在iOS开发中,可发起https对象请求的有以下对象:
    NSURLConnection
    NSURLSession
    NSWebView

3.2.1.NSURLConnection

    使用NSURLConnection进行https站点连接时,需要处理SSL验证,NSURLConnectionDelegate中提供了如下方法来处理这些验证逻辑。
    - connection: canAuthenticateAgainstProtectionSpace:
    - connection: didReceiveAuthenticationChallenge:
    具体示例如下:

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
  return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
3.2.1.1.接受任何证书
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
3.2.1.2.接受私有证书
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
        static CFArrayRef certs;
        if (!certs) {
            NSData *certData =[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"证书名称" ofType:@"cer"]];
            SecCertificateRef rootcert =SecCertificateCreateWithData(kCFAllocatorDefault,CFBridgingRetain(certData));
            const void *array[1] = { rootcert };
            certs = CFArrayCreate(NULL, array, 1, &kCFTypeArrayCallBacks);
            CFRelease(rootcert);   
        }

        SecTrustRef trust = [[challenge protectionSpace] serverTrust];
        int err;
        SecTrustResultType trustResult = 0;
        err = SecTrustSetAnchorCertificates(trust, certs);
        if (err == noErr) {
            err = SecTrustEvaluate(trust,&trustResult);
        }
        CFRelease(trust);
        BOOL trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed)||(trustResult == kSecTrustResultConfirm) || (trustResult == kSecTrustResultUnspecified));

        if (trusted) {
            [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
        }else{
            [challenge.sender cancelAuthenticationChallenge:challenge];
        }
}
3.2.2.NSURLSession

    类似于NSURLConnection, NSURLSessionDelegate中也提供了代理方法来处理验证逻辑。
    - NSURLSession: didReceiveChallenge:CompletionHandler:

    具体示例代码如下:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
         completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
{
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    NSURLCredential *credential = nil;
    
    NSString *trustDomain = @"request配置的host";
    
    if (trustDomain) {
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:trustDomain]) {
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                disposition = NSURLSessionAuthChallengeUseCredential;
            }
        }
    }
    
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain
{
    NSMutableArray *polocies = [NSMutableArray array];
    if (domain) {
        [polocies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else {
        [polocies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
    }
    
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)polocies);
    
    SecTrustResultType bindResult;
    SecTrustEvaluate(serverTrust, &bindResult);
    
    return (bindResult == kSecTrustResultUnspecified || bindResult == kSecTrustResultProceed);
}
3.2.3.UIWebView

    UIWebView不同于以上两个对象,没有预留相关的代理方法来实现https证书验证。
    但是,我们可以借助NSURLConnection对象发起请求以验证证书。验证逻辑示意图如下:

webview加载流程.png

    通常,我们使用UIWebView页面加载时使用的是http请求,直接使用loadRequest:进行请求操作,流程如图中虚线所示。
    现调整为https协议的请求,具体的页面加载和证书验证流程如下步骤:
    (1)在UIWebViewDelegate的代理方法webView: shouldStartLoadWithRequest: navigationType:中取消Web页面加载请求,创建NSURLConnection对象并发起https请求。
    (2)在NSURLConnection中进行证书验证,具体如3.2.1。
    (3)在NSURLConnection的代理方法connection: didReceiveResponse:中恢复Web请求。

    具体实现代码如下:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    
    NSLog(@"开始加载: %@ 授权:%d", [[request URL] absoluteString], _authenticated);
    if (!_authenticated) {
        _urlConnection = [[NSURLConnection alloc] initWithRequest:_requestW delegate:self];
        [_urlConnection start];
        return NO;
    }
    return YES;
}

NSURLConnection 中处理SSL验证

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace{
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
    if ([challenge previousFailureCount] == 0){
        NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
    } else{
        [[challenge sender] cancelAuthenticationChallenge:challenge];
    }
}

恢复UIWebView请求

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    _authenticated = YES;
    [self loadRequest:_requestW];
    [_urlConnection cancel];
}

4.讨论

4.1.配置本机host

    在XCode模拟器上使用ip发起https请求时,需要配置本机的host,使用命令vim /etc/hosts编辑并保存host配置文件。

4.2.证书验证次数

    在项目https适配的过程中发现,当在局域网内使用https请求是,同一ip,只需要验证一次证书,而在公网使用https请求时,每次请求都需进行这书验证。
    因此建议,不管在局域网和公网,都按照每次都需验证证书的逻辑来处理。

4.3.https请求耗时

    从https工作原理可以看出,https的流程要比http多了几个步骤,因此,总体来说,每次的https请求延迟要比http延迟时间要长,因为涉及到证书验证和数据的加解密过程。
    查阅相关参考资料,https的SSL握手(64ms)大概是TCP握手(22ms)耗时的3倍左右。
    毫秒级的延迟,对用户来说是基本感觉不到的,因此可以忽略不计。

5.参考资料

    http://www.cocoachina.com/ios/20150810/12947.html
    http://www.ttlsa.com/web/ssl-delay-how-long/

    在阅读的过程中,如若对该文章有技术错误或值得优化的建议,欢迎大家加QQ:690091622 进行技术交流和探讨。

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

推荐阅读更多精彩内容