最近服务器由原来的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 *)sessiondidReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler{}
afnetworking源码里面这个方法只有一行代码,把它替换成
- (void)URLSession:(NSURLSession *)sessiondidReceiveChallenge:(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); }];}
成功!