iOS https
目前iOS大都是用的AFN来进行数据请求
正常请求
配置
AFHTTPSessionManager * mgr = [AFHTTPSessionManager manager];
mgr.responseSerializer = [AFHTTPResponseSerializer serializer];
mgr.requestSerializer = [AFHTTPRequestSerializer serializer];
NSString * URL = @"https://www.baidu.com";
//中间穿插一层 SSL配置
//typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
// AFSSLPinningModeNone, 什么也不操作
// AFSSLPinningModePublicKey, 只验证公钥
// AFSSLPinningModeCertificate, 验证证书
//};
//此方法会默认加载本地的.cer文件
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}
return [NSSet setWithSet:certificates];
}
//并存到pinnedPublicKeys字段 公钥集合
- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
_pinnedCertificates = pinnedCertificates;
if (self.pinnedCertificates) {
NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
for (NSData *certificate in self.pinnedCertificates) {
id publicKey = AFPublicKeyForCertificate(certificate);
if (!publicKey) {
continue;
}
[mutablePinnedPublicKeys addObject:publicKey];
}
self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
} else {
self.pinnedPublicKeys = nil;
}
}
AFSecurityPolicy * p =
[AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
//验证证书中的域名
p.validatesDomainName = YES;
//是否允许过期的证书使用
p.allowInvalidCertificates = YES;
mgr.securityPolicy = p;
[mgr GET:URL parameters:@{} progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {}];
逻辑处理
主要使用是在 AFURLSessionManager.m 文件
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
{
//证书处理方式
//1 NSURLSessionAuthChallengeUseCredential //使用指定证书 可能为空
//2 NSURLSessionAuthChallengePerformDefaultHandling //如果没有实现代理方法默认处理, credential参数忽略
//3 NSURLSessionAuthChallengeCancelAuthenticationChallenge //所有的请求都会被取消, credential参数忽略
//4 NSURLSessionAuthChallengeRejectProtectionSpace //拒绝当前这一次, 下一次再询问
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 如果验证方法为NSURLAuthenticationMethodServerTrust,说明服务器端需要客户端返回一个根据认证挑战的保护空间提供的信任(即challenge.protectionSpace.serverTrust)产生的挑凭据,用+credentialForTrust方法创建
// 如果验证方法不是NSURLAuthenticationMethodServerTrust,则challenge.protectionSpace.serverTrust为nil
// 一般情况下,服务器的验证方式都是ServerTrust,如果不是,就用其它方法创建凭据(参考那个官方文档链接)
//参数的主要逻辑判断
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
//使用证书
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
//默认处理
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
//不符合验证条件的 但不符合的直接拒绝请求
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
//默认处理
//验证方法不是NSURLAuthenticationMethodServerTrust
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
evaluateServerTrust逻辑
//no 默认处理
//yes 根据空间域创建证书 有证书就验证,, 没有也是默认处理
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
//有域名 允许无效证书 校验域名 (不校验或者本地没证书)
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
// https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
// According to the docs, you should only trust your provided certs for evaluation.
// Pinned certificates are added to the trust. Without pinned certificates,
// there is nothing to evaluate against.
//
// From Apple Docs:
// "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
// Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;//无法访问
}
NSMutableArray *policies = [NSMutableArray array];
//校验域名
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
//不校验域名
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
//设置SSL检查策略 插入域名校验或常规
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
//如果模式设置为None
if (self.SSLPinningMode == AFSSLPinningModeNone) {
//当 允许无效的证书(自建证书) 直接不校验通过
//证书直接通过
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
//校验没通过 并且只能校验证书有效 不通过
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;//不通过
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
////将读取的证书设置为serverTrust的根证书
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
if (!AFServerTrustIsValid(serverTrust)) {
return NO;//先验证不通过
}
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
//证书base64校验
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;//不通过
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
}
AFPublicKeyForCertificate 本地证书提取公钥
static id AFPublicKeyForCertificate(NSData *certificate) {
id allowedPublicKey = nil;
SecCertificateRef allowedCertificate;
SecPolicyRef policy = nil;
SecTrustRef allowedTrust = nil;
SecTrustResultType result;
allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
__Require_Quiet(allowedCertificate != NULL, _out);
policy = SecPolicyCreateBasicX509();
__Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
_out:
if (allowedTrust) {
CFRelease(allowedTrust);
}
if (policy) {
CFRelease(policy);
}
if (allowedCertificate) {
CFRelease(allowedCertificate);
}
return allowedPublicKey;
}
AFServerTrustIsValid 策略
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
BOOL isValid = NO;
SecTrustResultType result;
//类似goto语句
__Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
//指示评估成功,证书隐式可信,但未显式指定用户意图
//Indicates the evaluation succeeded and the certificate is implicitly trusted, but user intent was not explicitly specified. This value may be returned by the SecTrustEvaluate function or stored as part of the user trust settings.
//表示您可以继续
//Indicates you may proceed. This value may be returned by the SecTrustEvaluate function or stored as part of the user trust settings.
isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
_out:
return isValid;
}
AFCertificateTrustChainForServerTrust 策略转化
static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
[trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
}
return [NSArray arrayWithArray:trustChain];
}
AFPublicKeyTrustChainForServerTrust 公钥
static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
SecPolicyRef policy = SecPolicyCreateBasicX509();
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
SecCertificateRef someCertificates[] = {certificate};
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);
SecTrustRef trust;
__Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
SecTrustResultType result;
__Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
[trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
_out:
if (trust) {
CFRelease(trust);
}
if (certificates) {
CFRelease(certificates);
}
continue;
}
CFRelease(policy);
return [NSArray arrayWithArray:trustChain];
}