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;
}