• iOS适配https详解


      马上就要元旦了,网上流传元旦之后苹果会对所有的app进行https的验证,据说会拒绝所有没有使用https的app。但是后来又听说是我们开发者误解了,元旦过后还是会支持http,不过开发者需要说明为什么不用https。不过躲得过初一躲不过十五,还是早点适配https好些啊。然后弄了几天找了好多博客和文档,才暂时解决了这个问题。所谓https,只不过是在http的基础上增加了ssl层的验证(这样说不是很准确),也就是在原来的数据包的基础上加密了一下,然而加密的工作不需要我们开发者来做,只需在对的位置做好证书的验证就行了。其实我对ssl层理解也不深,想着以后有时间一定要把网络这一块掌握,不然下次碰到这种问题还是不好解决。废话不多说,还是先看看代码吧。

      首先是对NSURLConnection的适配,不管是原生的还是AF的,归根结底都是要用它去连接服务器。

      1.如果使用AF进行网络数据请求,那么使用如下方法即可:

     1 - (AFSecurityPolicy*)customSecurityPolicy
     2 
     3 {
     4     //先导入证书
     5     //在这加证书,一般情况适用于单项认证
     6     NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"ailian" ofType:@"cer"];//证书的路径
     7 
     8     NSData *certData = [NSData dataWithContentsOfFile:cerPath];
     9     if (certData==nil) {
    10         return nil;
    11     }
    12     // AFSSLPinningModeCertificate 使用证书验证模式
    13     
    14     AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    15     
    16     // allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
    17     
    18     // 如果是需要验证自建证书,需要设置为YES
    19     
    20     securityPolicy.allowInvalidCertificates = YES;
    21     
    22     //validatesDomainName 是否需要验证域名,默认为YES;
    23     
    24     //假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
    25     
    26     //置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
    27     
    28     //如置为NO,建议自己添加对应域名的校验逻辑。
    29     
    30     securityPolicy.validatesDomainName = NO;
    31     
    32     securityPolicy.pinnedCertificates = @[certData];
    33     
    34     return securityPolicy;
    35     
    36 }

      其实,在这里不需要使用服务器的证书,本人亲测过。不过为了保险起见还是加上,还需要注意一点,AF3.0需要用到der格式的证书。

      然后加上下面的代码:

      _manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:[self getHostURL]]];

          [_manager setSecurityPolicy:[self customSecurityPolicy]];

      2.如果是原生的NSURLConnection,那么需要在NSURLConnectionDelegate的一个方法里面加代码,如下:

     1 - (void)connection:(NSURLConnection *)connection
     2 willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
     3 {
     4     NSString *thePath = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
     5     //导入证书       NSLog(@"thePath===========%@",thePath);
     6     NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
     7     CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
     8     
     9     SecIdentityRef identity = NULL;
    10     // extract the ideneity from the certificate
    11     [self extractP12Data:inPKCS12Data toIdentity:&identity];
    12     
    13     SecCertificateRef certificate = NULL;
    14     SecIdentityCopyCertificate (identity, &certificate);
    15     
    16     const void *certs[] = {certificate};
    17     //                        CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
    18     // create a credential from the certificate and ideneity, then reply to the challenge with the credential
    19     //NSLog(@"identity=========%@",identity);
    20     NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:nil persistence:NSURLCredentialPersistencePermanent];
    21     
    22     //           credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
    23     
    24     [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
    25 }          

      接下来是对NSURLSession的适配,现在公认更好的网络请求类,支持后台的数据下载和上传,而且自身是安全的。然而,在使用NSURLSession时需要在一个代理方法里配置ssl证书。如下:

     1 - (void)URLSession:(NSURLSession *)session
     2 didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
     3  completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
     4 {
     5     NSString *method = challenge.protectionSpace.authenticationMethod;
     6     if([method isEqualToString:NSURLAuthenticationMethodServerTrust]){
     7         NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
     8         completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
     9         return;
    10     }
    11     NSString *thePath = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
    12     NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
    13     CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(PKCS12Data);
    14     SecIdentityRef identity;
    15     // 读取p12证书中的内容
    16     OSStatus result = [self extractP12Data:inPKCS12Data toIdentity:&identity];
    17     if(result != errSecSuccess){
    18     completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    19         return;
    20     }
    21     SecCertificateRef certificate = NULL;
    22     SecIdentityCopyCertificate (identity, &certificate);
    23     const void *certs[] = {certificate};
    24     CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
    25     NSURLCredential *credential1 = [NSURLCredential credentialWithIdentity:identity certificates:(NSArray*)CFBridgingRelease(certArray) persistence:NSURLCredentialPersistencePermanent];
    26     completionHandler(NSURLSessionAuthChallengeUseCredential, credential1);
    27 }
     1 - (OSStatus) extractP12Data:(CFDataRef)inP12Data toIdentity:(SecIdentityRef*)identity {
     2     OSStatus securityError = errSecSuccess;
     3     CFStringRef password = CFSTR("clientepass");
     4     const void *keys[] = { kSecImportExportPassphrase };
     5     const void *values[] = { password };
     6     CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
     7     CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
     8     securityError = SecPKCS12Import(inP12Data, options, &items);
     9     if (securityError == 0) {
    10         CFDictionaryRef ident = (CFDictionaryRef)CFArrayGetValueAtIndex(items,0);
    11         const void *tempIdentity = NULL;
    12         tempIdentity = CFDictionaryGetValue(ident, kSecImportItemIdentity);
    13         *identity = (SecIdentityRef)tempIdentity;
    14     }
    15     if (options) {
    16         CFRelease(options);
    17     }
    18     return securityError;
    19 }

      其实适配https没有我们想象的那么复杂,你找对了地方可能几分钟就弄好了,找不到,可能花几天。不管怎样,最后适配成功还是要感谢网上的一些大神,虽然官网上面也有答案,但毕竟时间不等人,等我研究透彻,估计苹果又会有新的东西出来吧。至此,希望能帮到正在为https适配而忧伤的小伙伴们。

  • 相关阅读:
    Codeforces 1009F Dominant Indices
    C++之++运算符重载问题
    Codeforces 1010D Mars rover
    这是一个开始
    MoreEffectiveC++Item35(异常)(条款9-15)
    C++隐式类类型转化
    MoreEffectiveC++Item35(操作符)(条款5-8)
    MoreEffectiveC++Item35(基础议题)(条款1-4)
    php+mysql网站无限级栏目分类-递归获取树形结构函数
    JavaScript简易动画
  • 原文地址:https://www.cnblogs.com/lmfboke/p/6232656.html
Copyright © 2020-2023  润新知