美文网首页Hybrid开发
RN集成自签名https及双向认证-ios(1)

RN集成自签名https及双向认证-ios(1)

作者: Mickqingzhou | 来源:发表于2018-06-20 17:59 被阅读86次

    这里不对https和双向认证做理论解释,直接写具体的操作.

    问题分析 :

    RN中的网络部分都得进行证书认证处理

    1. Fetch
    2. react-native-fetch-blob
    3. webview
      这三者都得单独对对应的ios和android源码进行处理,这篇文章先讲ios的.

    首先将client.p12(认证客户端用的)server.cer(服务端的自签名证书,ios这边我没用它)导入工程里

    一 : Fetch

    27BF2A9B-BF9D-4CE4-BD04-7A015DE8D044.png
    /**
     * Copyright (c) 2015-present, Facebook, Inc.
     * All rights reserved.
     *
     * This source code is licensed under the BSD-style license found in the
     * LICENSE file in the root directory of this source tree. An additional grant
     * of patent rights can be found in the PATENTS file in the same directory.
     */
    
    #import "RCTHTTPRequestHandler.h"
    
    #import <mutex>
    
    #import "RCTNetworking.h"
    
    @interface RCTHTTPRequestHandler () <NSURLSessionDataDelegate>
    
    @end
    
    @implementation RCTHTTPRequestHandler
    {
      NSMapTable *_delegates;
      NSURLSession *_session;
      std::mutex _mutex;
    }
    
    @synthesize bridge = _bridge;
    
    RCT_EXPORT_MODULE()
    
    - (void)invalidate
    {
      [_session invalidateAndCancel];
      _session = nil;
    }
    
    - (BOOL)isValid
    {
      // if session == nil and delegates != nil, we've been invalidated
      return _session || !_delegates;
    }
    
    #pragma mark - NSURLRequestHandler
    
    - (BOOL)canHandleRequest:(NSURLRequest *)request
    {
      static NSSet<NSString *> *schemes = nil;
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
        // technically, RCTHTTPRequestHandler can handle file:// as well,
        // but it's less efficient than using RCTFileRequestHandler
        schemes = [[NSSet alloc] initWithObjects:@"http", @"https", nil];
      });
      return [schemes containsObject:request.URL.scheme.lowercaseString];
    }
    
    - (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request
                             withDelegate:(id<RCTURLRequestDelegate>)delegate
    {
      // Lazy setup
      if (!_session && [self isValid]) {
        NSOperationQueue *callbackQueue = [NSOperationQueue new];
        callbackQueue.maxConcurrentOperationCount = 1;
        callbackQueue.underlyingQueue = [[_bridge networking] methodQueue];
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        _session = [NSURLSession sessionWithConfiguration:configuration
                                                 delegate:self
                                            delegateQueue:callbackQueue];
        
        std::lock_guard<std::mutex> lock(_mutex);
        _delegates = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory
                                               valueOptions:NSPointerFunctionsStrongMemory
                                                   capacity:0];
      }
      
      NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];
      {
        std::lock_guard<std::mutex> lock(_mutex);
        [_delegates setObject:delegate forKey:task];
      }
      [task resume];
      return task;
    }
    
    - (void)cancelRequest:(NSURLSessionDataTask *)task
    {
      {
        std::lock_guard<std::mutex> lock(_mutex);
        [_delegates removeObjectForKey:task];
      }
      [task cancel];
    }
    
    #pragma mark - NSURLSession delegate
    
    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
       didSendBodyData:(int64_t)bytesSent
        totalBytesSent:(int64_t)totalBytesSent
    totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
    {
      id<RCTURLRequestDelegate> delegate;
      {
        std::lock_guard<std::mutex> lock(_mutex);
        delegate = [_delegates objectForKey:task];
      }
      [delegate URLRequest:task didSendDataWithProgress:totalBytesSent];
    }
    
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)task
    didReceiveResponse:(NSURLResponse *)response
     completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
    {
      id<RCTURLRequestDelegate> delegate;
      {
        std::lock_guard<std::mutex> lock(_mutex);
        delegate = [_delegates objectForKey:task];
      }
      [delegate URLRequest:task didReceiveResponse:response];
      completionHandler(NSURLSessionResponseAllow);
    }
    
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)task
        didReceiveData:(NSData *)data
    {
      id<RCTURLRequestDelegate> delegate;
      {
        std::lock_guard<std::mutex> lock(_mutex);
        delegate = [_delegates objectForKey:task];
      }
      [delegate URLRequest:task didReceiveData:data];
    }
    
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
    {
      id<RCTURLRequestDelegate> delegate;
      {
        std::lock_guard<std::mutex> lock(_mutex);
        delegate = [_delegates objectForKey:task];
        [_delegates removeObjectForKey:task];
      }
      [delegate URLRequest:task didCompleteWithError:error];
    }
    - (void)URLSession:(NSURLSession *)session
    didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
     completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
    {
      //挑战处理类型为 默认
      /*
       NSURLSessionAuthChallengePerformDefaultHandling:默认方式处理
       NSURLSessionAuthChallengeUseCredential:使用指定的证书
       NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消挑战
       */
      __weak typeof(self) weakSelf = self;
      NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
      __block NSURLCredential *credential = nil;
      
      // sessionDidReceiveAuthenticationChallenge是自定义方法,用来如何应对服务器端的认证挑战
      
      // 而这个证书就需要使用credentialForTrust:来创建一个NSURLCredential对象
      if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        
        // 创建挑战证书(注:挑战方式为UseCredential和PerformDefaultHandling都需要新建挑战证书)
        credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        // 确定挑战的方式
        if (credential) {
          //证书挑战  设计policy,none,则跑到这里
          disposition = NSURLSessionAuthChallengeUseCredential;
        } else {
          disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
      } else {
        // client authentication
        SecIdentityRef identity = NULL;
        SecTrustRef trust = NULL;
        NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
        NSFileManager *fileManager =[NSFileManager defaultManager];
        
        if(![fileManager fileExistsAtPath:p12])
        {
          NSLog(@"client.p12:not exist");
        }
        else
        {
          NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
          
          if ([[weakSelf class]extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
          {
            SecCertificateRef certificate = NULL;
            SecIdentityCopyCertificate(identity, &certificate);
            const void*certs[] = {certificate};
            CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
            credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge  NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
            disposition =NSURLSessionAuthChallengeUseCredential;
          }
        }
        
      }
      
      //完成挑战
      if (completionHandler) {
        completionHandler(disposition, credential);
      }
    }
    +(BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
      OSStatus securityError = errSecSuccess;
      //client certificate password
      NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"cplh123456"
                                                                   forKey:(__bridge id)kSecImportExportPassphrase];
      
      CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
      securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);
      
      if(securityError == 0) {
        CFDictionaryRef myIdentityAndTrust = (CFDictionaryRef)CFArrayGetValueAtIndex(items,0);
        const void*tempIdentity =NULL;
        tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
        *outIdentity = (SecIdentityRef)tempIdentity;
        const void*tempTrust =NULL;
        tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
        *outTrust = (SecTrustRef)tempTrust;
      } else {
        NSLog(@"Failedwith error code %d",(int)securityError);
        return NO;
      }
      return YES;
    }
    
    @end
    
    

    二 : react-native-fetch-blob

    28ACF9B1-E9B8-4BEF-8CA1-9B3EFF61CD6A.png
    //
    //  RNFetchBlobNetwork.m
    //  RNFetchBlob
    //
    //  Created by wkh237 on 2016/6/6.
    //  Copyright © 2016 wkh237. All rights reserved.
    //
    
    
    #import <Foundation/Foundation.h>
    #import "RNFetchBlob.h"
    #import "RNFetchBlobFS.h"
    #import "RNFetchBlobNetwork.h"
    #import "RNFetchBlobConst.h"
    #import "RNFetchBlobReqBuilder.h"
    #import "IOS7Polyfill.h"
    #import <CommonCrypto/CommonDigest.h>
    #import "RNFetchBlobProgress.h"
    
    #if __has_include(<React/RCTAssert.h>)
    #import <React/RCTRootView.h>
    #import <React/RCTLog.h>
    #import <React/RCTEventDispatcher.h>
    #import <React/RCTBridge.h>
    #else
    #import "RCTRootView.h"
    #import "RCTLog.h"
    #import "RCTEventDispatcher.h"
    #import "RCTBridge.h"
    #endif
    
    ////////////////////////////////////////
    //
    //  HTTP request handler
    //
    ////////////////////////////////////////
    
    NSMapTable * taskTable;
    NSMapTable * expirationTable;
    NSMutableDictionary * progressTable;
    NSMutableDictionary * uploadProgressTable;
    
    __attribute__((constructor))
    static void initialize_tables() {
        if(expirationTable == nil)
        {
            expirationTable = [[NSMapTable alloc] init];
        }
        if(taskTable == nil)
        {
            taskTable = [[NSMapTable alloc] init];
        }
        if(progressTable == nil)
        {
            progressTable = [[NSMutableDictionary alloc] init];
        }
        if(uploadProgressTable == nil)
        {
            uploadProgressTable = [[NSMutableDictionary alloc] init];
        }
    }
    
    
    typedef NS_ENUM(NSUInteger, ResponseFormat) {
        UTF8,
        BASE64,
        AUTO
    };
    
    
    @interface RNFetchBlobNetwork ()
    {
        BOOL * respFile;
        BOOL isNewPart;
        BOOL * isIncrement;
        NSMutableData * partBuffer;
        NSString * destPath;
        NSOutputStream * writeStream;
        long bodyLength;
        NSMutableDictionary * respInfo;
        NSInteger respStatus;
        NSMutableArray * redirects;
        ResponseFormat responseFormat;
        BOOL * followRedirect;
        BOOL backgroundTask;
    }
    
    @end
    
    @implementation RNFetchBlobNetwork
    
    NSOperationQueue *taskQueue;
    @synthesize taskId;
    @synthesize expectedBytes;
    @synthesize receivedBytes;
    @synthesize respData;
    @synthesize callback;
    @synthesize bridge;
    @synthesize options;
    @synthesize fileTaskCompletionHandler;
    @synthesize dataTaskCompletionHandler;
    @synthesize error;
    
    
    // constructor
    - (id)init {
        self = [super init];
        if(taskQueue == nil) {
            taskQueue = [[NSOperationQueue alloc] init];
            taskQueue.maxConcurrentOperationCount = 10;
        }
        return self;
    }
    
    + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config
    {
        if(progressTable == nil)
        {
            progressTable = [[NSMutableDictionary alloc] init];
        }
        [progressTable setValue:config forKey:taskId];
    }
    
    + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config
    {
        if(uploadProgressTable == nil)
        {
            uploadProgressTable = [[NSMutableDictionary alloc] init];
        }
        [uploadProgressTable setValue:config forKey:taskId];
    }
    
    // removing case from headers
    + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers
    {
        
        NSMutableDictionary * mheaders = [[NSMutableDictionary alloc]init];
        for(NSString * key in headers) {
            [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]];
        }
        
        return mheaders;
    }
    
    - (NSString *)md5:(NSString *)input {
        const char* str = [input UTF8String];
        unsigned char result[CC_MD5_DIGEST_LENGTH];
        CC_MD5(str, (CC_LONG)strlen(str), result);
        
        NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2];
        for(int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
            [ret appendFormat:@"%02x",result[I]];
        }
        return ret;
    }
    
    // send HTTP request
    - (void) sendRequest:(__weak NSDictionary  * _Nullable )options
           contentLength:(long) contentLength
                  bridge:(RCTBridge * _Nullable)bridgeRef
                  taskId:(NSString * _Nullable)taskId
             withRequest:(__weak NSURLRequest * _Nullable)req
                callback:(_Nullable RCTResponseSenderBlock) callback
    {
        self.taskId = taskId;
        self.respData = [[NSMutableData alloc] initWithLength:0];
        self.callback = callback;
        self.bridge = bridgeRef;
        self.expectedBytes = 0;
        self.receivedBytes = 0;
        self.options = options;
        
        backgroundTask = [options valueForKey:@"IOSBackgroundTask"] == nil ? NO : [[options valueForKey:@"IOSBackgroundTask"] boolValue];
        followRedirect = [options valueForKey:@"followRedirect"] == nil ? YES : [[options valueForKey:@"followRedirect"] boolValue];
        isIncrement = [options valueForKey:@"increment"] == nil ? NO : [[options valueForKey:@"increment"] boolValue];
        redirects = [[NSMutableArray alloc] init];
        if(req.URL != nil)
            [redirects addObject:req.URL.absoluteString];
        
        // set response format
        NSString * rnfbResp = [req.allHTTPHeaderFields valueForKey:@"RNFB-Response"];
        if([[rnfbResp lowercaseString] isEqualToString:@"base64"])
            responseFormat = BASE64;
        else if([[rnfbResp lowercaseString] isEqualToString:@"utf8"])
            responseFormat = UTF8;
        else
            responseFormat = AUTO;
        
        NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
        NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
        NSString * key = [self.options valueForKey:CONFIG_KEY];
        __block NSURLSession * session;
        
        bodyLength = contentLength;
        
        // the session trust any SSL certification
        NSURLSessionConfiguration *defaultConfigObject;
        
        defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
        
        if(backgroundTask)
        {
            defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
        }
        
        // set request timeout
        float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
        if(timeout > 0)
        {
            defaultConfigObject.timeoutIntervalForRequest = timeout/1000;
        }
        defaultConfigObject.HTTPMaximumConnectionsPerHost = 10;
        session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:taskQueue];
        if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil)
        {
            respFile = YES;
            
            NSString* cacheKey = taskId;
            if (key != nil) {
                cacheKey = [self md5:key];
                if (cacheKey == nil) {
                    cacheKey = taskId;
                }
                
                destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
                if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) {
                    callback(@[[NSNull null], RESP_TYPE_PATH, destPath]);
                    return;
                }
            }
            
            if(path != nil)
                destPath = path;
            else
                destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
        }
        else
        {
            respData = [[NSMutableData alloc] init];
            respFile = NO;
        }
        
        __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
        [taskTable setObject:task forKey:taskId];
        [task resume];
        
        // network status indicator
        if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES)
            [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
        __block UIApplication * app = [UIApplication sharedApplication];
        
    }
    
    // #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled
    + (void) emitExpiredTasks
    {
        NSEnumerator * emu =  [expirationTable keyEnumerator];
        NSString * key;
        
        while((key = [emu nextObject]))
        {
            RCTBridge * bridge = [RNFetchBlob getRCTBridge];
            NSData * args = @{ @"taskId": key };
            [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args];
            
        }
        
        // clear expired task entries
        [expirationTable removeAllObjects];
        expirationTable = [[NSMapTable alloc] init];
        
    }
    
    ////////////////////////////////////////
    //
    //  NSURLSession delegates
    //
    ////////////////////////////////////////
    
    
    #pragma mark NSURLSession delegate methods
    
    
    #pragma mark - Received Response
    // set expected content length on response received
    - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
    {
        expectedBytes = [response expectedContentLength];
        
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
        NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
        NSString * respType = @"";
        respStatus = statusCode;
        if ([response respondsToSelector:@selector(allHeaderFields)])
        {
            NSDictionary *headers = [httpResponse allHeaderFields];
            NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString];
            if(self.isServerPush == NO)
            {
                self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"];
            }
            if(self.isServerPush)
            {
                if(partBuffer != nil)
                {
                    [self.bridge.eventDispatcher
                     sendDeviceEventWithName:EVENT_SERVER_PUSH
                     body:@{
                            @"taskId": taskId,
                            @"chunk": [partBuffer base64EncodedStringWithOptions:0],
                            }
                     ];
                }
                partBuffer = [[NSMutableData alloc] init];
                completionHandler(NSURLSessionResponseAllow);
                return;
            }
            if(respCType != nil)
            {
                NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
                if([respCType RNFBContainsString:@"text/"])
                {
                    respType = @"text";
                }
                else if([respCType RNFBContainsString:@"application/json"])
                {
                    respType = @"json";
                }
                // If extra blob content type is not empty, check if response type matches
                else if( extraBlobCTypes !=  nil) {
                    for(NSString * substr in extraBlobCTypes)
                    {
                        if([respCType RNFBContainsString:[substr lowercaseString]])
                        {
                            respType = @"blob";
                            respFile = YES;
                            destPath = [RNFetchBlobFS getTempPath:taskId withExtension:nil];
                            break;
                        }
                    }
                }
                else
                {
                    respType = @"blob";
                    // for XMLHttpRequest, switch response data handling strategy automatically
                    if([options valueForKey:@"auto"] == YES) {
                        respFile = YES;
                        destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""];
                    }
                }
            }
            else
                respType = @"text";
            respInfo = @{
                         @"taskId": taskId,
                         @"state": @"2",
                         @"headers": headers,
                         @"redirects": redirects,
                         @"respType" : respType,
                         @"timeout" : @NO,
                         @"status": [NSNumber numberWithInteger:statusCode]
                         };
            
    #pragma mark - handling cookies
            // # 153 get cookies
            if(response.URL != nil)
            {
                NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
                NSArray<NSHTTPCookie *> * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL];
                if(cookies != nil && [cookies count] > 0) {
                    [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil];
                }
            }
            
            [self.bridge.eventDispatcher
             sendDeviceEventWithName: EVENT_STATE_CHANGE
             body:respInfo
             ];
            headers = nil;
            respInfo = nil;
            
        }
        else
            NSLog(@"oops");
        
        if(respFile == YES)
        {
            @try{
                NSFileManager * fm = [NSFileManager defaultManager];
                NSString * folder = [destPath stringByDeletingLastPathComponent];
                if(![fm fileExistsAtPath:folder])
                {
                    [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
                }
                BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue];
                BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"];
                
                appendToExistingFile = !overwrite;
                
                // For solving #141 append response data if the file already exists
                // base on PR#139 @kejinliang
                if(appendToExistingFile)
                {
                    destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""];
                }
                if (![fm fileExistsAtPath:destPath])
                {
                    [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
                }
                writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile];
                [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
                [writeStream open];
            }
            @catch(NSException * ex)
            {
                NSLog(@"write file error");
            }
        }
        
        completionHandler(NSURLSessionResponseAllow);
    }
    
    
    // download progress handler
    - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
    {
        // For #143 handling multipart/x-mixed-replace response
        if(self.isServerPush)
        {
            [partBuffer appendData:data];
            return ;
        }
        
        NSNumber * received = [NSNumber numberWithLong:[data length]];
        receivedBytes += [received longValue];
        NSString * chunkString = @"";
        
        if(isIncrement == YES)
        {
            chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        }
        
        if(respFile == NO)
        {
            [respData appendData:data];
        }
        else
        {
            [writeStream write:[data bytes] maxLength:[data length]];
        }
        RNFetchBlobProgress * pconfig = [progressTable valueForKey:taskId];
        if(expectedBytes == 0)
            return;
        NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)];
        if(pconfig != nil && [pconfig shouldReport:now])
        {
            [self.bridge.eventDispatcher
             sendDeviceEventWithName:EVENT_PROGRESS
             body:@{
                    @"taskId": taskId,
                    @"written": [NSString stringWithFormat:@"%d", receivedBytes],
                    @"total": [NSString stringWithFormat:@"%d", expectedBytes],
                    @"chunk": chunkString
                    }
             ];
        }
        received = nil;
        
    }
    
    - (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
    {
        if([session isEqual:session])
            session = nil;
    }
    
    
    - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
    {
        
        self.error = error;
        NSString * errMsg = [NSNull null];
        NSString * respStr = [NSNull null];
        NSString * rnfbRespType = @"";
        
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
        
        if(respInfo == nil)
        {
            respInfo = [NSNull null];
        }
        
        if(error != nil)
        {
            errMsg = [error localizedDescription];
        }
        
        if(respFile == YES)
        {
            [writeStream close];
            rnfbRespType = RESP_TYPE_PATH;
            respStr = destPath;
        }
        // base64 response
        else {
            // #73 fix unicode data encoding issue :
            // when response type is BASE64, we should first try to encode the response data to UTF8 format
            // if it turns out not to be `nil` that means the response data contains valid UTF8 string,
            // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
            NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
            
            if(responseFormat == BASE64)
            {
                rnfbRespType = RESP_TYPE_BASE64;
                respStr = [respData base64EncodedStringWithOptions:0];
            }
            else if (responseFormat == UTF8)
            {
                rnfbRespType = RESP_TYPE_UTF8;
                respStr = utf8;
            }
            else
            {
                if(utf8 != nil)
                {
                    rnfbRespType = RESP_TYPE_UTF8;
                    respStr = utf8;
                }
                else
                {
                    rnfbRespType = RESP_TYPE_BASE64;
                    respStr = [respData base64EncodedStringWithOptions:0];
                }
            }
        }
        
        
        callback(@[ errMsg, rnfbRespType, respStr]);
        
        @synchronized(taskTable, uploadProgressTable, progressTable)
        {
            if([taskTable objectForKey:taskId] == nil)
                NSLog(@"object released by ARC.");
            else
                [taskTable removeObjectForKey:taskId];
            [uploadProgressTable removeObjectForKey:taskId];
            [progressTable removeObjectForKey:taskId];
        }
        
        respData = nil;
        receivedBytes = 0;
        [session finishTasksAndInvalidate];
        
    }
    
    // upload progress handler
    - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite
    {
        RNFetchBlobProgress * pconfig = [uploadProgressTable valueForKey:taskId];
        if(totalBytesExpectedToWrite == 0)
            return;
        NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
        if(pconfig != nil && [pconfig shouldReport:now]) {
            [self.bridge.eventDispatcher
             sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD
             body:@{
                    @"taskId": taskId,
                    @"written": [NSString stringWithFormat:@"%d", totalBytesWritten],
                    @"total": [NSString stringWithFormat:@"%d", totalBytesExpectedToWrite]
                    }
             ];
        }
    }
    
    + (void) cancelRequest:(NSString *)taskId
    {
        NSURLSessionDataTask * task = [taskTable objectForKey:taskId];
        if(task != nil && task.state == NSURLSessionTaskStateRunning)
            [task cancel];
    }
    
    
    - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
    {
        BOOL trusty = [options valueForKey:CONFIG_TRUSTY];
        if(!trusty)
        {
            completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
        }
        else
        {
            //        completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
            
            [self ZQURLSession:session didReceiveChallenge:challenge completionHandler:completionHandler];
        }
    }
    
    
    - (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
    {
        NSLog(@"sess done in background");
    }
    
    - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
    {
        
        if(followRedirect)
        {
            if(request.URL != nil)
                [redirects addObject:[request.URL absoluteString]];
            completionHandler(request);
        }
        else
        {
            completionHandler(nil);
        }
    }
    - (void)ZQURLSession:(NSURLSession *)session
     didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
       completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
    {
        //挑战处理类型为 默认
        /*
         NSURLSessionAuthChallengePerformDefaultHandling:默认方式处理
         NSURLSessionAuthChallengeUseCredential:使用指定的证书
         NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消挑战
         */
        __weak typeof(self) weakSelf = self;
        NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        __block NSURLCredential *credential = nil;
        
        // sessionDidReceiveAuthenticationChallenge是自定义方法,用来如何应对服务器端的认证挑战
        
        // 而这个证书就需要使用credentialForTrust:来创建一个NSURLCredential对象
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            
            // 创建挑战证书(注:挑战方式为UseCredential和PerformDefaultHandling都需要新建挑战证书)
            credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            // 确定挑战的方式
            if (credential) {
                //证书挑战  设计policy,none,则跑到这里
                disposition = NSURLSessionAuthChallengeUseCredential;
            } else {
                disposition = NSURLSessionAuthChallengePerformDefaultHandling;
            }
        } else {
            // client authentication
            SecIdentityRef identity = NULL;
            SecTrustRef trust = NULL;
            NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
            NSFileManager *fileManager =[NSFileManager defaultManager];
            
            if(![fileManager fileExistsAtPath:p12])
            {
                NSLog(@"client.p12:not exist");
            }
            else
            {
                NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
                
                if ([[weakSelf class]extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
                {
                    SecCertificateRef certificate = NULL;
                    SecIdentityCopyCertificate(identity, &certificate);
                    const void*certs[] = {certificate};
                    CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
                    credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge  NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
                    disposition =NSURLSessionAuthChallengeUseCredential;
                }
            }
            
        }
        
        //完成挑战
        if (completionHandler) {
            completionHandler(disposition, credential);
        }
    }
    +(BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
        OSStatus securityError = errSecSuccess;
        //client certificate password
        NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"cplh123456"
                                                                     forKey:(__bridge id)kSecImportExportPassphrase];
        
        CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
        securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);
        
        if(securityError == 0) {
            CFDictionaryRef myIdentityAndTrust = (CFDictionaryRef)CFArrayGetValueAtIndex(items,0);
            const void*tempIdentity =NULL;
            tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
            *outIdentity = (SecIdentityRef)tempIdentity;
            const void*tempTrust =NULL;
            tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
            *outTrust = (SecTrustRef)tempTrust;
        } else {
            NSLog(@"Failedwith error code %d",(int)securityError);
            return NO;
        }
        return YES;
    }
    
    @end
    
    

    注意: 在RN里使用fetch-blob时得配置trusty : true

    三 : webview

    D81CCB48-FEE5-48CE-8E4C-2031D01AC704.png
    9CA02E39-BCD3-4116-BA0A-F668777D59F0.png
    /**
     * Copyright (c) 2015-present, Facebook, Inc.
     * All rights reserved.
     *
     * This source code is licensed under the BSD-style license found in the
     * LICENSE file in the root directory of this source tree. An additional grant
     * of patent rights can be found in the PATENTS file in the same directory.
     */
    
    #import "RCTWebView.h"
    
    #import <UIKit/UIKit.h>
    
    #import "RCTAutoInsetsProtocol.h"
    #import "RCTConvert.h"
    #import "RCTEventDispatcher.h"
    #import "RCTLog.h"
    #import "RCTUtils.h"
    #import "RCTView.h"
    #import "UIView+React.h"
    
    NSString *const RCTJSNavigationScheme = @"react-js-navigation";
    NSString *const RCTJSPostMessageHost = @"postMessage";
    
    @interface RCTWebView () <UIWebViewDelegate, RCTAutoInsetsProtocol,NSURLConnectionDataDelegate>
    
    @property (nonatomic, copy) RCTDirectEventBlock onLoadingStart;
    @property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish;
    @property (nonatomic, copy) RCTDirectEventBlock onLoadingError;
    @property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest;
    @property (nonatomic, copy) RCTDirectEventBlock onMessage;
    
    //  https验证
    @property (nonatomic,strong) NSURLConnection *urlConnection;
    @property (nonatomic,strong) NSURLRequest *requestW;
    @property (nonatomic) SSLAuthenticate authenticated;
    
    @end
    
    @implementation RCTWebView
    {
      UIWebView *_webView;
      NSString *_injectedJavaScript;
      
    }
    
    - (void)dealloc
    {
      _webView.delegate = nil;
    }
    
    - (instancetype)initWithFrame:(CGRect)frame
    {
      if ((self = [super initWithFrame:frame])) {
        super.backgroundColor = [UIColor clearColor];
        _automaticallyAdjustContentInsets = YES;
        _contentInset = UIEdgeInsetsZero;
        _webView = [[UIWebView alloc] initWithFrame:self.bounds];
        _webView.delegate = self;
        [self addSubview:_webView];
      }
      return self;
    }
    
    RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
    
    - (void)goForward
    {
      [_webView goForward];
    }
    
    - (void)goBack
    {
      [_webView goBack];
    }
    
    - (void)reload
    {
      NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
      if (request.URL && !_webView.request.URL.absoluteString.length) {
        _requestW = request;
        [_webView loadRequest:request];
      }
      else {
        [_webView reload];
      }
    }
    
    - (void)stopLoading
    {
      [_webView stopLoading];
    }
    
    - (void)postMessage:(NSString *)message
    {
      NSDictionary *eventInitDict = @{
                                      @"data": message,
                                      };
      NSString *source = [NSString
                          stringWithFormat:@"document.dispatchEvent(new MessageEvent('message', %@));",
                          RCTJSONStringify(eventInitDict, NULL)
                          ];
      [_webView stringByEvaluatingJavaScriptFromString:source];
    }
    
    - (void)injectJavaScript:(NSString *)script
    {
      [_webView stringByEvaluatingJavaScriptFromString:script];
    }
    
    - (void)setSource:(NSDictionary *)source
    {
      if (![_source isEqualToDictionary:source]) {
        _source = [source copy];
        
        // Check for a static html source first
        NSString *html = [RCTConvert NSString:source[@"html"]];
        if (html) {
          NSURL *baseURL = [RCTConvert NSURL:source[@"baseUrl"]];
          if (!baseURL) {
            baseURL = [NSURL URLWithString:@"about:blank"];
          }
          [_webView loadHTMLString:html baseURL:baseURL];
          return;
        }
        
        NSURLRequest *request = [RCTConvert NSURLRequest:source];
        _requestW = request;
        // Because of the way React works, as pages redirect, we actually end up
        // passing the redirect urls back here, so we ignore them if trying to load
        // the same url. We'll expose a call to 'reload' to allow a user to load
        // the existing page.
        if ([request.URL isEqual:_webView.request.URL]) {
          return;
        }
        if (!request.URL) {
          // Clear the webview
          
          [_webView loadHTMLString:@"" baseURL:nil];
          return;
        }
        [_webView loadRequest:request];
      }
    }
    
    - (void)layoutSubviews
    {
      [super layoutSubviews];
      _webView.frame = self.bounds;
    }
    
    - (void)setContentInset:(UIEdgeInsets)contentInset
    {
      _contentInset = contentInset;
      [RCTView autoAdjustInsetsForView:self
                        withScrollView:_webView.scrollView
                          updateOffset:NO];
    }
    
    - (void)setScalesPageToFit:(BOOL)scalesPageToFit
    {
      if (_webView.scalesPageToFit != scalesPageToFit) {
        _webView.scalesPageToFit = scalesPageToFit;
        [_webView reload];
      }
    }
    
    - (BOOL)scalesPageToFit
    {
      return _webView.scalesPageToFit;
    }
    
    - (void)setBackgroundColor:(UIColor *)backgroundColor
    {
      CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor);
      self.opaque = _webView.opaque = (alpha == 1.0);
      _webView.backgroundColor = backgroundColor;
    }
    
    - (UIColor *)backgroundColor
    {
      return _webView.backgroundColor;
    }
    
    - (NSMutableDictionary<NSString *, id> *)baseEvent
    {
      NSMutableDictionary<NSString *, id> *event = [[NSMutableDictionary alloc] initWithDictionary:@{
                                                                                                     @"url": _webView.request.URL.absoluteString ?: @"",
                                                                                                     @"loading" : @(_webView.loading),
                                                                                                     @"title": [_webView stringByEvaluatingJavaScriptFromString:@"document.title"],
                                                                                                     @"canGoBack": @(_webView.canGoBack),
                                                                                                     @"canGoForward" : @(_webView.canGoForward),
                                                                                                     }];
      
      return event;
    }
    
    - (void)refreshContentInset
    {
      [RCTView autoAdjustInsetsForView:self
                        withScrollView:_webView.scrollView
                          updateOffset:YES];
    }
    
    #pragma mark - UIWebViewDelegate methods
    
    - (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
     navigationType:(UIWebViewNavigationType)navigationType
    {
      if ([request.URL.scheme rangeOfString:@"https"].location != NSNotFound) {
        if (!_authenticated) {
          //    _authenticated = NO;
          //    __weak typeof(self) weakSelf = self;
          //开启同步的请求去双向认证
          NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
          
          [conn start];
          
          [webView stopLoading];
          
          return NO;
        }else{
          
          BOOL isJSNavigation = [request.URL.scheme isEqualToString:RCTJSNavigationScheme];
          
          static NSDictionary<NSNumber *, NSString *> *navigationTypes;
          static dispatch_once_t onceToken;
          dispatch_once(&onceToken, ^{
            navigationTypes = @{
                                @(UIWebViewNavigationTypeLinkClicked): @"click",
                                @(UIWebViewNavigationTypeFormSubmitted): @"formsubmit",
                                @(UIWebViewNavigationTypeBackForward): @"backforward",
                                @(UIWebViewNavigationTypeReload): @"reload",
                                @(UIWebViewNavigationTypeFormResubmitted): @"formresubmit",
                                @(UIWebViewNavigationTypeOther): @"other",
                                };
          });
          
          // skip this for the JS Navigation handler
          if (!isJSNavigation && _onShouldStartLoadWithRequest) {
            NSMutableDictionary<NSString *, id> *event = [self baseEvent];
            [event addEntriesFromDictionary: @{
                                               @"url": (request.URL).absoluteString,
                                               @"navigationType": navigationTypes[@(navigationType)]
                                               }];
            if (![self.delegate webView:self
              shouldStartLoadForRequest:event
                           withCallback:_onShouldStartLoadWithRequest]) {
              return NO;
            }
          }
          
          if (_onLoadingStart) {
            // We have this check to filter out iframe requests and whatnot
            BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
            if (isTopFrame) {
              NSMutableDictionary<NSString *, id> *event = [self baseEvent];
              [event addEntriesFromDictionary: @{
                                                 @"url": (request.URL).absoluteString,
                                                 @"navigationType": navigationTypes[@(navigationType)]
                                                 }];
              _onLoadingStart(event);
            }
          }
          
          if (isJSNavigation && [request.URL.host isEqualToString:RCTJSPostMessageHost]) {
            NSString *data = request.URL.query;
            data = [data stringByReplacingOccurrencesOfString:@"+" withString:@" "];
            data = [data stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
            
            NSMutableDictionary<NSString *, id> *event = [self baseEvent];
            [event addEntriesFromDictionary: @{
                                               @"data": data,
                                               }];
            _onMessage(event);
          }
          
          // JS Navigation handler
          return !isJSNavigation;
        }
        
      }else{
        BOOL isJSNavigation = [request.URL.scheme isEqualToString:RCTJSNavigationScheme];
        
        static NSDictionary<NSNumber *, NSString *> *navigationTypes;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
          navigationTypes = @{
                              @(UIWebViewNavigationTypeLinkClicked): @"click",
                              @(UIWebViewNavigationTypeFormSubmitted): @"formsubmit",
                              @(UIWebViewNavigationTypeBackForward): @"backforward",
                              @(UIWebViewNavigationTypeReload): @"reload",
                              @(UIWebViewNavigationTypeFormResubmitted): @"formresubmit",
                              @(UIWebViewNavigationTypeOther): @"other",
                              };
        });
        
        // skip this for the JS Navigation handler
        if (!isJSNavigation && _onShouldStartLoadWithRequest) {
          NSMutableDictionary<NSString *, id> *event = [self baseEvent];
          [event addEntriesFromDictionary: @{
                                             @"url": (request.URL).absoluteString,
                                             @"navigationType": navigationTypes[@(navigationType)]
                                             }];
          if (![self.delegate webView:self
            shouldStartLoadForRequest:event
                         withCallback:_onShouldStartLoadWithRequest]) {
            return NO;
          }
        }
        
        if (_onLoadingStart) {
          // We have this check to filter out iframe requests and whatnot
          BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
          if (isTopFrame) {
            NSMutableDictionary<NSString *, id> *event = [self baseEvent];
            [event addEntriesFromDictionary: @{
                                               @"url": (request.URL).absoluteString,
                                               @"navigationType": navigationTypes[@(navigationType)]
                                               }];
            _onLoadingStart(event);
          }
        }
        
        if (isJSNavigation && [request.URL.host isEqualToString:RCTJSPostMessageHost]) {
          NSString *data = request.URL.query;
          data = [data stringByReplacingOccurrencesOfString:@"+" withString:@" "];
          data = [data stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
          
          NSMutableDictionary<NSString *, id> *event = [self baseEvent];
          [event addEntriesFromDictionary: @{
                                             @"data": data,
                                             }];
          _onMessage(event);
        }
        
        // JS Navigation handler
        return !isJSNavigation;
      }
      
      
    }
    
    - (void)webView:(__unused UIWebView *)webView didFailLoadWithError:(NSError *)error
    {
      if (_onLoadingError) {
        if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) {
          // NSURLErrorCancelled is reported when a page has a redirect OR if you load
          // a new URL in the WebView before the previous one came back. We can just
          // ignore these since they aren't real errors.
          // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os
          return;
        }
        
        NSMutableDictionary<NSString *, id> *event = [self baseEvent];
        [event addEntriesFromDictionary:@{
                                          @"domain": error.domain,
                                          @"code": @(error.code),
                                          @"description": error.localizedDescription,
                                          }];
        _onLoadingError(event);
      }
    }
    
    - (void)webViewDidFinishLoad:(UIWebView *)webView
    {
      if (_messagingEnabled) {
    #if RCT_DEV
        // See isNative in lodash
        NSString *testPostMessageNative = @"String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')";
        BOOL postMessageIsNative = [
                                    [webView stringByEvaluatingJavaScriptFromString:testPostMessageNative]
                                    isEqualToString:@"true"
                                    ];
        if (!postMessageIsNative) {
          RCTLogError(@"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined");
        }
    #endif
        NSString *source = [NSString stringWithFormat:
                            @"window.originalPostMessage = window.postMessage;"
                            "window.postMessage = function(data) {"
                            "window.location = '%@://%@?' + encodeURIComponent(String(data));"
                            "};", RCTJSNavigationScheme, RCTJSPostMessageHost
                            ];
        [webView stringByEvaluatingJavaScriptFromString:source];
      }
      if (_injectedJavaScript != nil) {
        NSString *jsEvaluationValue = [webView stringByEvaluatingJavaScriptFromString:_injectedJavaScript];
        
        NSMutableDictionary<NSString *, id> *event = [self baseEvent];
        event[@"jsEvaluationValue"] = jsEvaluationValue;
        
        _onLoadingFinish(event);
      }
      // we only need the final 'finishLoad' call so only fire the event when we're actually done loading.
      else if (_onLoadingFinish && !webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) {
        _onLoadingFinish([self baseEvent]);
      }
    }
    
    
    
    #pragma mark ***NSURLConnection代理方法***
    - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
      
      NSURLCredential * credential;
      
      assert(challenge != nil);
      
      credential = nil;
      
      NSLog(@"----收到质询----");
      
      NSString *authenticationMethod = [[challenge protectionSpace] authenticationMethod];
      
      if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        
        NSLog(@"----服务器验证客户端----");
        
        NSString *host = challenge.protectionSpace.host;
        
        SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
        
        
        NSMutableArray *polices = [NSMutableArray array];
        
        if (NO) {
          
          [polices addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)host)];
          
        }else{
          
          [polices addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
          
        }
        
        SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)polices);
        
        //导入证书
        NSString *path = [[NSBundle mainBundle] pathForResource:@"CNPCCA" ofType:@"cer"];
        
        NSData *certData = [NSData dataWithContentsOfFile:path];
        
        NSMutableArray *pinnedCerts = [NSMutableArray arrayWithObjects:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData), nil];
        
        SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCerts);
        
        credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        
      } else {
        
        NSLog(@"----客户端验证服务端----");
        
        SecIdentityRef identity = NULL;
        
        SecTrustRef trust = NULL;
        
        NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
        
        NSFileManager *fileManager = [NSFileManager defaultManager];
        
        if (![fileManager fileExistsAtPath:p12]) {
          
          NSLog(@"客户端.p12证书 不存在!");
          
        }else{
          
          NSData *pkcs12Data = [NSData dataWithContentsOfFile:p12];
          
          if ([self extractIdentity:&identity andTrust:&trust fromPKCS12Data:pkcs12Data]) {
            
            SecCertificateRef certificate = NULL;
            
            SecIdentityCopyCertificate(identity, &certificate);
            
            const void *certs[] = {certificate};
            
            CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
            
            credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray *)certArray persistence:NSURLCredentialPersistencePermanent];
          }
          
        }
        
      }
      
      [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
      
    }
    
    - (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
      
      OSStatus securityErr = errSecSuccess;
      
      //输入客户端证书密码
      NSDictionary *optionsDic = [NSDictionary dictionaryWithObject:@"cplh123456"  forKey:(__bridge id)kSecImportExportPassphrase];
      
      CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
      
      securityErr = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data, (__bridge CFDictionaryRef)optionsDic, &items);
      
      if (securityErr == errSecSuccess) {
        
        CFDictionaryRef mineIdentAndTrust = CFArrayGetValueAtIndex(items, 0);
        
        const void *tmpIdentity = NULL;
        
        tmpIdentity = CFDictionaryGetValue(mineIdentAndTrust, kSecImportItemIdentity);
        
        *outIdentity = (SecIdentityRef)tmpIdentity;
        
        const void *tmpTrust = NULL;
        
        tmpTrust = CFDictionaryGetValue(mineIdentAndTrust, kSecImportItemTrust);
        
        *outTrust = (SecTrustRef)tmpTrust;
        
      }else{
        
        return false;
        
      }
      
      return true;
      
    }
    
    
    -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)pResponse {
      
      _authenticated = YES;
      
      //webview 重新加载请求。
      //
      //  NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
      //
      //  [[session dataTaskWithRequest:_requestW completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
      //
      //    NSLog(@"%s",__FUNCTION__);
      //    NSLog(@"RESPONSE:%@",response);
      //    NSLog(@"ERROR:%@",error);
      //
      //    NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
      //    NSLog(@"dataString:%@",dataString);
      //
      //    [self loadHTMLString:dataString baseURL:nil];
      //  }] resume];
      
      [_webView loadRequest:_requestW];
      
      [connection cancel];
      
    }
    
    @end
    
    

    相关文章

      网友评论

        本文标题:RN集成自签名https及双向认证-ios(1)

        本文链接:https://www.haomeiwen.com/subject/afbxyftx.html