首页 > 代码库 > AFNetWorking3.0使用 自签名证书的https请求

AFNetWorking3.0使用 自签名证书的https请求

      前几日,项目组出于安全角度的考虑,要求项目中的请求使用https请求,因为是企业内部使用的app,因此使用了自签名的证书,而自签名的证书是不受信任的,所以我们就需要自己来做证书的验证,包括服务器验证客户端的证书和我们要信任服务器的证书,SSL双向认证的原理我这里就不赘述了,这里提供两篇博客

    iOS安全系列之一:HTTPS: http://oncenote.com/2014/10/21/Security-1-HTTPS/

    iOS安全系列之二:HTTPS进阶: http://oncenote.com/2015/09/16/Security-2-HTTPS2/

      从开始准备到调试成功一共用了两天,查阅了很多网上提供的解决办法,最后发现很多解决办法都缺少了一个很重要的过程,就是将客户端的证书发送到服务器,让服务器验证我们的身份.如果没有这一步,那么我们的请求就会直接被服务器拒绝导致请求失败.

  话不多说,相信说多了也没人看..[手动滑稽]直接上代码,我这里用了afn3.0

  准备工作:

    首先要导入两个证书, 是两个  一个是客户端使用的.p12的证书,另外一个是服务器端的.cer的证书,将这两个证书拖入工程中,注意要添加target

    然后要配置ATS,ATS的配置我也提供一篇博客

    iOS 10 适配 ATS: http://www.jianshu.com/p/36ddc5b009a7

           新建一个类,继承AFHTTPSessionManager, 我这里重写了initWithURL

 1 - (instancetype)initWithBaseURL:(NSURL *)url {
 2     self = [super initWithBaseURL:url];
 3     if (self) {
 4         
 5         self.requestSerializer.timeoutInterval = 25;
 6         [self.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
 7         [self.requestSerializer setValue:url.absoluteString forHTTPHeaderField:@"Referer"];
 8         
 9         self.securityPolicy = [self getFreeCustomSecurityPolicy];
10         
11         self.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", @"multipart/form-data", @"application/json", @"text/html", @"image/jpeg", @"image/png", @"application/octet-stream", @"text/json", nil];
12         self.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
13         
14         [self setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {
15             NSLog(@"setSessionDidBecomeInvalidBlock error : %@",error);
16         }];
17         if ([url.absoluteString hasPrefix:@"https"]) {//当请求是https时,我们就需要做
18             __weak typeof(self) weakSelf = self;
19             [self setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing  _Nullable * _Nullable credential) {
20                 NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
21                 __autoreleasing NSURLCredential * credentialSelf =nil;
22                 if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {//如果有受信任的证书,如果是自签名的证书,这里会是NSURLAuthenticationMethodClientCertificate

23 if([weakSelf.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
24                         credentialSelf = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
25                         if(credentialSelf) {
26                             disposition = NSURLSessionAuthChallengeUseCredential;
27                         } else {
28                             disposition = NSURLSessionAuthChallengePerformDefaultHandling;
29                         }
30                     } else {
31                         disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
32                     }
33                 } else {//当接收到的是NSURLAuthenticationMethodClientCertificate,我们就将我们的证书发送给服务器让其验证我们的身份
34                     // client authentication
35                     SecIdentityRef identity = NULL;
36                     SecTrustRef trust = NULL;
37                     NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];
38                     NSFileManager *fileManager =[NSFileManager defaultManager];
39                     if(![fileManager fileExistsAtPath:p12])
40                     {
41                         NSLog(@"client.p12:not exist");
42                     } else {
43                         NSData *PKCS12Data =http://www.mamicode.com/ [NSData dataWithContentsOfFile:p12];
44                         
45                         if ([weakSelf  extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
46                         {
47                             SecCertificateRef certificate = NULL;
48                             SecIdentityCopyCertificate(identity, &certificate);
49                             const void*certs[] = {certificate};
50                             CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
51                             credentialSelf =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge  NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
52                             disposition = NSURLSessionAuthChallengeUseCredential;
53                         }
54                     }
55                 }
56                 *credential = credentialSelf;
57                 return disposition;
58             }];
59         }
60     }
61     return self;
62 }
63 - (BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
64     OSStatus securityError = errSecSuccess;
65     //client certificate password
66     NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"password" forKey:(__bridge id)kSecImportExportPassphrase];
67     CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
68     securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);
69     
70     if(securityError == 0) {
71         CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
72         const void*tempIdentity =NULL;
73         tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
74         *outIdentity = (SecIdentityRef)tempIdentity;
75         const void*tempTrust =NULL;
76         tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
77         *outTrust = (SecTrustRef)tempTrust;
78     } else {
79         NSLog(@"Failedwith error code %d",(int)securityError);
80         return NO;
81     }
82     return YES;
83 }
84 - (AFSecurityPolicy *)getFreeCustomSecurityPolicy {
85     AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];
86     NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];
87     NSData *certData =http://www.mamicode.com/ [NSData dataWithContentsOfFile:cerPath];
88     NSSet *certificateSet  = [[NSSet alloc] initWithObjects:certData, nil];
89     [securityPolicy setAllowInvalidCertificates:YES];
90     [securityPolicy setPinnedCertificates:certificateSet];
91     [securityPolicy setValidatesDomainName:NO];
92     
93     return securityPolicy;
94 }

 

这样设置好了之后就可以发送使用自签名证书的https请求了 

希望可以帮助到遇到自签名证书无法发送请求的同学

代码不太优雅,轻喷,如有错误的地方欢迎指正

邮箱:masterLiPeng@163.com

AFNetWorking3.0使用 自签名证书的https请求