新技术论坛
搜索
查看: 3176|回复: 0
打印 上一主题 下一主题

[iOS/OS X] iOS AFNetworking框架HTTPS请求配置

[复制链接]
  • TA的每日心情
    开心
    2016-12-9 18:18
  • 签到天数: 85 天

    连续签到: 1 天

    [LV.6]常住居民II

    扫一扫,手机访问本帖
    楼主
    跳转到指定楼层
    发表于 2017-4-23 07:46:42 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式



       iOS在Apple公司的强制要求下,数据传输必须按照ATS(App Transefer  Security)条款。关于AFNetworking框架传输HTTPS数据。
      一.AllowsArbitraryLoads 白名单机制
      NSAllowsArbitraryLoads是ATS推广过程中的产物,当然也许可持续很久甚至永久,为了访问HTTP服务,一般需要绕过ATS限制,需要配置info.plist文件
      keyNSAppTransportSecurity/key dict         keyNSAllowsArbitraryLoads/key         true/  /dict  
      这种机制实际上是允许了所有HTTP  和HTTPS的访问,显然,这种做法实际上很危险。设置为false就能避免绕开ATS,问题是我们真的需要完全关闭这个选项么?
      比如某些文件服务器,CDN服务器配置HTTPS反而影响传输速度,这种情况下HTTP反而具有很高的优越性。因此,对于这类服务器的HTTP传输,我们其实也可以使用如下方式(设置白名单),白名单之外的必须使用HTTPS协议。
    1. <key>NSAppTransportSecurity</key>
    2.     <dict>
    3.         <key>NSExceptionDomains</key>
    4.         <dict>
    5.             <key>lib.baidu.com</key>
    6.             <dict>
    7.                 <key>NSIncludesSubdomains</key>
    8.                 <true/>
    9.             </dict>
    10.             <key>oss.fastdfs.cn</key>
    11.             <dict>
    12.                 <key>NSIncludesSubdomains</key>
    13.                 <true/>
    14.             </dict>
    15.            </dict>
    16.    </dict>  
    复制代码

      二.免证书验证
      免证书验证,一般来说是client证书库不会把server传输来的证书进行校验。
      举个例子
    1. AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    2. //允许非权威机构颁发的证书
    3. manager.securityPolicy.allowInvalidCertificates = YES;
    4. //也不验证域名一致性
    5. manager.securityPolicy.validatesDomainName = NO;
    6. //关闭缓存避免干扰测试
    7. manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

    8. [manager GET:@"https://www.baidu.com/s?wd=https" parameters:nil progress:nil  
    9. success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
    10.         NSLog(@"%@",responseObject);
    11.     } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    12.         NSLog(@"%@",error);
    13. }];  
    复制代码

      这种方式导致我们的app容易遭到中间人攻击,原因并不完全是我们设置了allowInvalidCertificates=YES  和validatesDomainName=NO,而是本地证书库中没有添加server的CA证书,只要是任意的https都能伪造为服务器。
      因此,我们并不推荐使用这种方式。
      三.证书验证
      3.1 加密标准分类
      证书验证分为2类,一类是单向认证,一类是双认证。
      此外,我们还需要区分证书与证书库的区别,证书库类型包括PKCS12,JKS,BKS等,其中,PKCS12是互联网标准,可以跨平台跨语言(支持Android,iOS,WP,PC,JAVA,PHP...),JKS是Java标准,只能在Java平台和Java语言下使用,BKS是  Bouncy  Castle公司的加密标准,作为Android平台支持SSL/TLS加密套件PKCS12的补充,其加密强度很高,但是目前只用于Android平台。证书的定义是保存在证书库中的数字签名信息或者单独的证书文件,如crt,pem,cer等文件。在说加密之前,先来看看自签名证书。
      3.2 自签名证书 vs 第三方权威机构证书
      有人可能会有疑问,自签名证书和第三方权威机构的证书的效用是否一样?
      实际上本人认为完全一样,在互联网中,基于B/S架构的服务中,B端通常导入了第三方权威机构的根证书(ROOT  CA),实际上就是为了验证网站的CA证书是不是安全的,并且验证是不是由权威的安全机构签发的证书。问题是,我们的App与Server是C/S架构,每个app最多也就只能访问有限的几个网址,我们自己做的app实际上本身就是信任自己的所设置的站点URL的,也就是说我们自己人相信自己人。我们的证书只是为了数据安全传输,不被中间人攻击,因此完全没必要使用(ROOT  CA),但是具体来说,到目前为止也没人试过这种方式是否可以通过审核。
      我试图找第三方CA证书机构交流,貌似他只是说这是苹果的规定,实际上自签名证书本质上没什么问题,只要密钥长度,复杂度,加密方式等达到要求即可。
      因此,苹果如果真的要求必须使用第三方CA  ROOT签名的规则,本身是不合理的。这些问题也没法避免,不过,如果苹果允许自签名证书的话,设置allowInvalidCertificates=YES即可。
      3.3 单向认证
      需要准备的文件:服务端证书库 , 服务端导出的证书
      单向认证,实际上说的是只有Client端对Server端的证书进行验证,Server不需要验证Client端的证书。
      自定义类:MyAFNetworking
    1. + (AFHTTPSessionManager *)manager;
    2. {
    3.     static AFHTTPSessionManager *shareInstance = nil;
    4.     static dispatch_once_t onceToken;
    5.     dispatch_once(&onceToken, ^{

    6.         NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    7.         shareInstance = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:BaseHttpURLString] sessionConfiguration:configuration];
    8.         //设置请求参数的类型:JSON
    9.         shareInstance.requestSerializer = [AFJSONRequestSerializer serializer];
    10.         //设置服务器返回结果的类型:JSON (AFJSONResponseSerializer,AFHTTPResponseSerializer)
    11.         shareInstance.responseSerializer = [AFJSONResponseSerializer serializer];
    12.         //设置请求的超时时间
    13.         shareInstance.requestSerializer.timeoutInterval = 20.0f;
    14.         //设置ContentType
    15.         shareInstance.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/plain", @"text/javascript", @"text/xml", @"image/jpeg",@"image/png", nil];

    16.         // https配置
    17.         NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"你的证书名" ofType:@"cer"];
    18.         NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    19.         AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[[NSSet alloc] initWithObjects:certData, nil]];

    20.         NSSet *dataSet = [[NSSet alloc] initWithObjects:certData, nil]; //这里可以添加多个server的证书

    21.         // setPinnedCertificates 设置证书文件(可能不止一个证书)
    22.         [securityPolicy setPinnedCertificates:dataSet];
    23.         // allowInvalidCertificates 是否允许无效证书
    24.         [securityPolicy setAllowInvalidCertificates:NO];
    25.         // validatesDomainName 是否需要验证域名
    26.         [securityPolicy setValidatesDomainName:YES];

    27.         shareInstance.securityPolicy = securityPolicy;
    28.     });
    29.     return shareInstance;
    30. }
    复制代码

      注意:以上说的证书是从服务器端到处的cer或者crt证书,这类证书是X509 Der格式的二进制编码证书,不是X509  PAM格式的Base64编码证书,关于自签名证书的生成请参考如下地址
      常见证书格式及相互转换
      iOS非对称加解密
      iOS 自签名证书建立(self-signed)
      3.4 双向认证
      iOS和Android一样,客户端证书库类型可以是PKCS12类型的pfx证书,此类证书包含私钥,公钥和证书,并且由密码。
      双向认证一般用于安全要求比较高的产品,比如金融类app,政府app等特殊行业。
      需要准备的文件:服务端证书库,服务端证书信任库 , 服务端导出的证书,客户端证书库,客户端证书
      注[1]:服务端证书库可以和服务端信任证书库使用同一个证书库,唯一要做的是把客户端证书导入进行。
      注[2]:客户端证书一般使用跨平台的PKCS12证书库(pfx或p12),必须记住证书库密钥,此类证书库同时包含私钥,公钥和证书。
      3.4.1 信任服务器
      本步骤用来校验客户端证书,和单向认证完全相同
      自定义类:MyAFNetworking
    1. + (AFHTTPSessionManager *)manager;
    2. {
    3.     static AFHTTPSessionManager *shareInstance = nil;
    4.     static dispatch_once_t onceToken;
    5.     dispatch_once(&onceToken, ^{

    6.         NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    7.         shareInstance = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:BaseHttpURLString] sessionConfiguration:configuration];
    8.         //设置请求参数的类型:JSON
    9.         shareInstance.requestSerializer = [AFJSONRequestSerializer serializer];
    10.         //设置服务器返回结果的类型:JSON (AFJSONResponseSerializer,AFHTTPResponseSerializer)
    11.         shareInstance.responseSerializer = [AFJSONResponseSerializer serializer];
    12.         //设置请求的超时时间
    13.         shareInstance.requestSerializer.timeoutInterval = 20.0f;
    14.         //设置ContentType
    15.         shareInstance.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/plain", @"text/javascript", @"text/xml", @"image/jpeg",@"image/png", nil];

    16.         // https配置
    17.         NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"你的证书名" ofType:@"cer"];
    18.         NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    19.         AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[[NSSet alloc] initWithObjects:certData, nil]];

    20.         NSSet *dataSet = [[NSSet alloc] initWithObjects:certData, nil]; //这里可以添加多个server的证书

    21.         // setPinnedCertificates 设置证书文件(可能不止一个证书)
    22.         [securityPolicy setPinnedCertificates:dataSet];
    23.         // allowInvalidCertificates 是否允许无效证书
    24.         [securityPolicy setAllowInvalidCertificates:NO];
    25.         // validatesDomainName 是否需要验证域名
    26.         [securityPolicy setValidatesDomainName:YES];

    27.         shareInstance.securityPolicy = securityPolicy;
    28.     });
    29.     return shareInstance;
    30. }
    复制代码

      3.4.2 提供客户端证书和证书库
    1. /*
    2. *
    3. **
    4. * 创建服务器信任客户端的认证条件
    5. **
    6. */
    7. +(AFHTTPSessionManager *) createCredentialsClient
    8. {
    9. __block AFHTTPSessionManager * manager = [MyAFNetworking manager];
    10. [manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
    11.     NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    12.     __autoreleasing NSURLCredential *credential =nil;
    13.     if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
    14.         if([manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
    15.             credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
    16.             if(credential) {
    17.                 disposition =NSURLSessionAuthChallengeUseCredential;
    18.             } else {
    19.                 disposition =NSURLSessionAuthChallengePerformDefaultHandling;
    20.             }
    21.         } else {
    22.             disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
    23.         }
    24.     } else {
    25.         // client authentication
    26.         SecIdentityRef identity = NULL;
    27.         SecTrustRef trust = NULL;
    28.         NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"pfx"];
    29.         NSFileManager *fileManager =[NSFileManager defaultManager];

    30.         if(![fileManager fileExistsAtPath:p12])
    31.         {
    32.             NSLog(@"client.p12:not exist");
    33.         }
    34.         else
    35.         {
    36.             NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
    37.             #加载PKCS12证书,pfx或p12
    38.             if ([MyAFNetworking extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
    39.             {
    40.                 SecCertificateRef certificate = NULL;
    41.                 SecIdentityCopyCertificate(identity, &certificate);
    42.                 const void*certs[] = {certificate};
    43.                 CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
    44.                 credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge  NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
    45.                 disposition =NSURLSessionAuthChallengeUseCredential;
    46.             }
    47.         }
    48.     }
    49.     *_credential = credential;
    50.     return disposition;
    51. }];

    52. return manager;
    53. }

    54. /**
    55. **加载PKCS12证书,pfx或p12
    56. **  
    57. **/
    58. +(BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
    59.     OSStatus securityError = errSecSuccess;
    60.     //client certificate password
    61.     NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"你的p12密码"
    62.                                                                 forKey:(__bridge id)kSecImportExportPassphrase];

    63.     CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    64.     securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);

    65.     if(securityError == 0) {
    66.         CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
    67.         const void*tempIdentity =NULL;
    68.         tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
    69.         *outIdentity = (SecIdentityRef)tempIdentity;
    70.         const void*tempTrust =NULL;
    71.         tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
    72.         *outTrust = (SecTrustRef)tempTrust;
    73.     } else {
    74.         NSLog(@"Failedwith error code %d",(int)securityError);
    75.         return NO;
    76.     }
    77.     return YES;
    78. }
    复制代码

      通过以上方式,我们便能实现双向认证了
    1. AFHTTPSessionManager * manager = [MyAFNetworking createCredentialsClient];  
    复制代码




    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    手机版|Archiver|开发者俱乐部 ( ICP/ISP证:辽B-2-4-20110106号 IDC证:辽B-1-2-20070003号 )

    GMT+8, 2024-12-23 01:56 , Processed in 0.154619 second(s), 21 queries .

    X+ Open Developer Network (xodn.com)

    © 2009-2017 沈阳讯网网络科技有限公司

    快速回复 返回顶部 返回列表