zoukankan      html  css  js  c++  java
  • 如何在iOS上实现对HTTPS的支持(转)

    原文地址:http://blog.5ibc.net/p/101504.html

    首先,需要明确你使用HTTP/HTTPS的用途,因为OSX和iOS平台提供了多种API,来支持不同的用途,官方文档《Making HTTP and HTTPS Requests》有详细的说明,而文档《HTTPS Server Trust Evaluation》则详细讲解了HTTPS验证相关知识,这里就不多说了。本文主要讲解我们最常用的NSURLConnection支持HTTPS的实现(NSURLSession的实现方法类似,只是要求授权证明的回调不一样而已),以及怎么样使用AFNetworking这个非常流行的第三方库来支持HTTPS。本文假设你对HTTP以及NSURLConnection的接口有了足够的了解。

    1. 验证证书的API

    相关的Api在Security Framework中,验证流程如下:

    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的数字签名未在操作系统的信任列表中。

    iOS授权验证的API和流程大概了解了,下面,我们看看在NSURLConnection中的代码实现:

    使用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];
      }
    }

    建议采用本地导入证书的方式验证证书,来保证足够的安全性。更多的验证方法,请查看官方文档《HTTPS Server Trust Evaluation》

    2. 使用AFNetworking来支持HTTPS

    AFNetworking是iOS/OSX开发最流行的第三方开源库之一,其作者是非常著名的iOS/OSX开发者Mattt Thompson,其博客NSHipster也是iOS/OSX开发者学习和开阔技术视野的好地方。AFNetworking已经将上面的逻辑代码封装好,甚至更完善,在AFSecurityPolicy文件中,有兴趣可以阅读这个模块的代码;

    AFNetworking上配置对HTTPS的支持非常简单:

    NSURL * url = [NSURL URLWithString:@"https://www.google.com"];
    AFHTTPRequestOperationManager * requestOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:url];
    dispatch_queue_t requestQueue = dispatch_create_serial_queue_for_name("kRequestCompletionQueue");
    requestOperationManager.completionQueue = requestQueue;
    AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    //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;
    requestOperationManager.securityPolicy = securityPolicy;

    这就是AFNetworking的支持HTTPS的主要配置说明,AFHTTPSessionManager与之基本一致,就不重复了。

    3. 总结

    从2017年1月起,iOS必须支持HTTPS,所以HTTPS已经是大势所趋了。

  • 相关阅读:
    转 linux下vi命令大全
    转 html5 canvas 详细使用教程
    怎么让手机网站自适应设备屏幕宽度? 转自百度经验
    转 :<meta http-equiv="X-UA-Compatible" content="IE=edge" /> 的说明
    转自haorooms :网页防止黑客跨框架攻击,及浏览器安全性想到的
    元信息标记<meta>
    Java语言的主要特性
    学习面向对象的三条主线之一 java类及类的成员
    1.5 MySQL信息源
    1.4在MySQL 8.0中添加,不建议使用或删除的服务器和状态变量及选项
  • 原文地址:https://www.cnblogs.com/xiaoxiaoyublogs/p/6222407.html
Copyright © 2011-2022 走看看