• AFNetworking3.0 Https P12证书


    最近服务器由原来的ice中间件改为https。方便了和服务器交互时不用自己aes加密了。

     -之前服务器人员和我(IOS)都没有使用过https,所以https跑不通很难说是服务器没有配置好还是IOS这边网络没有写好。

     -开始服务器提供了两个证书(自签名)。 一个.cer一个p12.

    导入AFNetworking 之后按照其他资料提供的方法很简单。

     1 NSString *urlString = @"https://xxxxx网址";
     2 NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"https" ofType:@"cer"];  //获取cer证书
     3 NSData * certData =[NSData dataWithContentsOfFile:cerPath];
     4 NSSet * certSet = [[NSSet alloc] initWithObjects:certData, nil];
     5 AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; //采用证书验证模式
     6 // 是否允许,NO-- 不允许无效的证书 YES-- 允许无效的证书(因为用的是自签名证书这里设置为YES,没有经过CA认证)
     7 [securityPolicy setAllowInvalidCertificates:YES];
     8 // 设置证书
     9 [securityPolicy setPinnedCertificates:certSet];
    10 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    11 manager.securityPolicy = securityPolicy;
    12 manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    13 // request 这里开始GET请求
    14 [manager GET:urlString parameters:nil progress:^(NSProgress * progress){
    15 } success:^(NSURLSessionDataTask *task, id responseObject) {
    16     NSLog(@"OK === %@",array); //请求成功
    17 } failure:^(NSURLSessionDataTask *task, NSError *error) {
    18     NSLog(@"error ==%@",error.description); //请求失败
    19 }];

      -经过我一番研究,太TM简单了啊添加几个参数就行了。

      -谁知道按照这样怎么也跑不通。调试了半天,都跑到afnetworking源码里面去查了,好像查出来证书验证通过不了。AFSecurityPolicy.h这个文件里面的 AFServerTrustIsValid方法就是通过不了,里面有个枚举值

    代表证书验证的结果

    typedef uint32_t SecTrustResultType;
    enum {
        kSecTrustResultInvalid = 0,
        kSecTrustResultProceed = 1,
        kSecTrustResultConfirm SEC_DEPRECATED_ATTRIBUTE = 2,
        kSecTrustResultDeny = 3,
        kSecTrustResultUnspecified = 4,
        kSecTrustResultRecoverableTrustFailure = 5,
        kSecTrustResultFatalTrustFailure = 6,
        kSecTrustResultOtherError = 7
    };

    具体枚举值什么意思直接百度就能搜出来。验证结果一直是kSecTrustResultRecoverableTrustFailure,5.意思就是证书验证失败。

      -开始怀疑服务器给的.cer证书有问题。

      -查了几天资料,找到一个只用p12证书的资料。(因为搜索资料时好多人说了证书的问题,所以怀疑和cer证书转换的格式或者编码有关,所以决定用原始的p12证书)

      -结果用原始的p12证书和原生的网络跑通了。用afnetworking的话资料都是用了cer,加上cer证书就跑不通。本来决定用原生网络,后来又要封装各种post啊什么的太麻烦了,所以去研究afnetworking和p12证书了。

      -最后解决。

      -afnetworking的AFURLSessionManager.m类里面的方法

    - (void)URLSession:(NSURLSession *)session
    didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
     completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
    {
    
    }

    afnetworking源码里面这个方法只有一行代码,把它替换成

    - (void)URLSession:(NSURLSession *)session
    didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
     completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
    {
        NSString *method = challenge.protectionSpace.authenticationMethod;
        NSLog(@">>>>1 %@", method);
        
        if([method isEqualToString:NSURLAuthenticationMethodServerTrust]){
            
            NSString *host = challenge.protectionSpace.host;
            NSLog(@">>>>2 %@", host);
            
            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
            return;
        } else if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
            
            NSString *thePath = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
            NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
            CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(PKCS12Data);
            SecIdentityRef identity;
            
            
            
            // 读取p12证书中的内容
            OSStatus result = [self extractP12Data:inPKCS12Data toIdentity:&identity];
            
            
            if(result != errSecSuccess){
                completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
                return;
            }
            
            
            SecCertificateRef certificate = NULL;
            SecIdentityCopyCertificate(identity, &certificate);
            
            const void *certs[] = {certificate};
            CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
            
            NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:(NSArray*)CFBridgingRelease(certArray) persistence:NSURLCredentialPersistencePermanent];
            
            completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
        }
    }

    这个是收到验证证书时候的回调。

    再在这个类里面添加一个方法

    -(OSStatus) extractP12Data:(CFDataRef)inP12Data toIdentity:(SecIdentityRef*)identity {
        
        OSStatus securityError = errSecSuccess;
        
        CFStringRef password = CFSTR("yaoguang123");
        const void *keys[] = { kSecImportExportPassphrase };
        const void *values[] = { password };
        
        CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
        
        CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
        securityError = SecPKCS12Import(inP12Data, options, &items);
        
        if (securityError == 0) {
            CFDictionaryRef ident = (CFDictionaryRef)CFArrayGetValueAtIndex(items,0);
            const void *tempIdentity = NULL;
            tempIdentity = CFDictionaryGetValue(ident, kSecImportItemIdentity);
            *identity = (SecIdentityRef)tempIdentity;
        }
        
        if (options) {
            CFRelease(options);
        }
        
        //    NSLog(@">>>>>>>> inP12Data = %@", inP12Data);
        //    NSLog(@">>>>>>>> identity = %@", identity);
        
        return securityError;
    }

    之后想要获取数据,不用设置验证模式啊什么的,直接调用

    - (void)testHttps {
    
        manager = [AFHTTPSessionManager manager];
        
        [manager GET:@"https://www.iyaoguang.com:8443/App/login?phone=123&password=123" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"???????????? %@",responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"?????????? %@",error);
        }];
    
    }

    成功!

  • 相关阅读:
    Spring Boot 2.4版本前后的分组配置变化及对多环境配置结构的影响
    Spring Boot 2.4 对多环境配置的支持更改
    Spring Boot 的2020最后一击:2.4.1、2.3.7、2.2.12 发布
    苹果M1芯片各种不支持,但居然可以刷朋友圈!你会买单吗?
    老板居然让我在Java项目中“造假”
    Spring Cloud正式移除Hystrix、Zuul等Netflix OSS组件
    为了Java微信支付V3开发包,我找出了微信支付文档至少六个错误
    IdentityServer4系列 | 支持数据持久化
    IdentityServer4系列 | 混合模式
    Gitlab Runner的分布式缓存实战
  • 原文地址:https://www.cnblogs.com/weak/p/6142508.html
Copyright © 2020-2023  润新知