在使用AFNetworking来作为网络请求库时,对于HTTPS请求的证书验证,我们需要去初始化继承于AFURLSessionManager的AFHTTPSessionManager
,设置好securityPolicy
, AFURLSessionManager里面有3种安全策略可供使用者选择:
@interface AFURLSessionManager : NSObject <NSURLSessionDelegate,...>
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
...
@end
@interface AFSecurityPolicy : NSObject <NSSecureCoding, NSCopying>
@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
...
@end
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone, // 不使用本地证书验证服务器
AFSSLPinningModePublicKey, // 使用本地证书里面的公钥验证服务器
AFSSLPinningModeCertificate, // 使用本地证书验证服务器
};
iOS 客户端在发送网络请求时,如果收到服务器的质询(NSURLAuthenticationChallenge
)时会调用NSURLSessionDelegate
里面的代理方法, AFNetworking
安全策略的实现就是在这个方法里面让初始化时设置的securityPolicy
去完成的。(NSURLSessionTaskDelegate
里面的那个方法原理一样所以这里不讲)
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
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 {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
通过源码分析可知其实现原理:
第一步:在初始化AFHTTPSessionManager
时就初始化securityPolicy
对象,设置好需要的属性。
第二步:在需要验证时调用securityPolicy
对象方法- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain
第三步:具体的验证方式,分三种情况进行:
情况一: AFSSLPinningModeNone,
// APP内部没放证书,要验证服务器证书
返回验证结果是:
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
// 即(允许服务器无效证书) || (不允许无效证书但证书验证通过)为YES ,否则为NO;
情况二:AFSSLPinningModeCertificate,
// APP内部放了证书,要验证服务器证书,且证书要跟APP内部的证书比对
第一步:将本地的证书设置为服务器信任`serverTrust`的锚点证书。
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
第二步:对设置了锚点证书的`serverTrust`进行服务器证书验证
BOOL IsValid = AFServerTrustIsValid(serverTrust);
第三步:上一步验证通过后,将服务器证书从`serverTrust`获取出来,并与本地证书作比对,比对结果作为返回值。
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
情况三:AFSSLPinningModeCertificate,
// APP内部放了证书,要验证服务器证书,且要将服务器证书里面的公钥跟APP内部证书的公钥比对
第一步: 在服务器证书验证通过后,将服务器证书的公钥从`serverTrust`中取出
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
第二步:本地证书的公钥是在初始化的时候就提取出来了,所以这里直接将本地证书中公钥与publicKeys比对,将结果作为返回值.
网友评论