iOS中防止中间人攻击

2018/4/10 posted in  HTTP

NSURLSession / NSURLConnection 防止中间人攻击,废话不多说直接上代码。
1、客户端发起网络请求

- (void)sendPostRequest:(NSString *)baseURL body:(NSString *)body BackGround:(BOOL)isBack completion:(void (^)(NSDictionary *dict))completion onError:(void (^)(NSError *error))onError
{
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
     
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
                                                          delegate:self
                                                     delegateQueue:self.sessionQueue];
     
    NSString *requestString = [NSString stringWithFormat:@"%@",baseURL];
     NSData *bodyData = [body dataUsingEncoding:NSUTF8StringEncoding];
    NSURL *url = [NSURL URLWithString:requestString];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
     
    [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
    [request setHTTPShouldHandleCookies:NO];
     
    [request setHTTPMethod:@"POST"];
    [request setTimeoutInterval:10];
    [request setHTTPBody:bodyData];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
     
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                                                 
                                                [Util complete_handler:response
                                                               baseurl:baseURL
                                                                  data:data
                                                                 error:error
                                                            completion:completion
                                                               onError:onError];
                                            }];
    [task resume];
 
}

在这里不需要解释太多NSURLSession / NSURLConnection 大同小异,如果这都有难度,下面的请忽略。

2、在接手做这个东西的时候由于国内关于这方面的资料几乎没有,然后只能自己查阅相关文档。
AFNetworking 会比系统提供的简单许多,在这里就不解释了。这里主要是对NSURLSession 进行描述。
我们主要对其代理方法:

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

下面就是详细步骤;

    #pragma mark - NSURLSessionDelegate 
//用过CF的东西,你一定要记得释放呀,不然你就太水了 

复制代码
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler
{
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        do {
//读取本地证书
            NSString *path = [[NSBundle mainBundle] pathForResource:@"Geo_Trust_Der" ofType:@"der"];
            NSData *certificateData = [NSData dataWithContentsOfFile:path];
            SecCertificateRef localCertificate =  SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certificateData);
//这个方法后面会有代码
            SecKeyRef localKey = [self getPublicKeyWithCertificateData:localCertificate]; 
            SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
             
            NSMutableArray *serverCertificates = [NSMutableArray array];
            CFIndex certCount = SecTrustGetCertificateCount(serverTrust);
//证书链  什么?这你都不知道? 好吧 ,百度一下就知道了
            for (CFIndex certIndex = 0; certIndex < certCount; certIndex++) {
                SecCertificateRef   thisCertificate;                
                thisCertificate = SecTrustGetCertificateAtIndex(serverTrust, certIndex);
                [serverCertificates addObject:(__bridge id)thisCertificate];
            }
            SecCertificateRef serverCertificate = (__bridge SecCertificateRef)([serverCertificates lastObject]);
/*读取公钥,在这里为什么这么说呢 ,证书有很多东西可以读取,包括指纹等等,不知道? 自行百度
*/
            SecKeyRef serverKey = [self getPublicKeyWithCertificateData:serverCertificate];
//这里我们判断两个公钥的二进制数据是否一致 
            if ([[self getPublicKeyBitsFromKey:localKey] isEqualToData:[self getPublicKeyBitsFromKey:serverKey]]) {
                NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
                [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
                completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
            } else {
                [challenge.sender cancelAuthenticationChallenge:challenge];
            }
            CFRelease(serverKey);
             
            CFRelease(localKey);
            CFRelease(localCertificate);
        } while (0);
    }   
}

至此你已经顺利完成了。那我现在附上上面的两个功能函数
//获取证书

- (SecKeyRef)getPublicKeyWithCertificateData:(SecCertificateRef)myCertificate
{
    SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
    SecTrustRef myTrust;
    OSStatus status = SecTrustCreateWithCertificates(myCertificate,myPolicy,&myTrust);
    SecTrustResultType trustResult;
    if (status == noErr) {
        SecTrustEvaluate(myTrust, &trustResult);
    }
    SecKeyRef localPBKey = SecTrustCopyPublicKey(myTrust);
    CFRelease(myPolicy);
    CFRelease(myTrust);
     
    return localPBKey;
}

//读取公钥数据

- (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey {
     
    static const uint8_t publicKeyIdentifier[] = "随便写!";
    NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
     
    OSStatus sanityCheck = noErr;
    NSData * publicKeyBits = nil;
     
    NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
    [queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
     
    // Temporarily add key to the Keychain, return as data:
    NSMutableDictionary * attributes = [queryPublicKey mutableCopy];
    [attributes setObject:(__bridge id)givenKey forKey:(__bridge id)kSecValueRef];
    [attributes setObject:@YES forKey:(__bridge id)kSecReturnData];
    CFTypeRef result;
    sanityCheck = SecItemAdd((__bridge CFDictionaryRef) attributes, &result);
    if (sanityCheck == errSecSuccess) {
        publicKeyBits = CFBridgingRelease(result);
         
        // Remove from Keychain again:
        (void)SecItemDelete((__bridge CFDictionaryRef) queryPublicKey);
    }
     
    return publicKeyBits;
}