该文章阅读的AFNetworking的版本为3.2.0。html
AFSecurityPolicy
这个类是用于验证HTTPS请求的证书是否正确。安全
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
};
复制代码
这个枚举定义了服务器证书验证模式:bash
AFSSLPinningModeNone
不使用证书验证服务器服务器
AFSSLPinningModePublicKey
使用证书中的公钥验证服务器app
AFSSLPinningModeCertificate
使用证书验证服务器dom
/**
服务器证书验证模式,默认是不验证
*/
@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
/**
验证服务器的证书的集合,默认状况下,AFNetworking会搜索工程中全部.cer的证书文件,但不会将某个证书做为默认。若是想建立AFSecurityPolicy对象,就先调用certificatesInBundle方法加载证书,而后调用policyWithPinningMode:withPinnedCertificates方法建立对象
*/
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
/**
是否信任无效或者过时的证书,默认为否
*/
@property (nonatomic, assign) BOOL allowInvalidCertificates;
/**
是否验证证书中的域名,默认为是
*/
@property (nonatomic, assign) BOOL validatesDomainName;
复制代码
/**
用这个方法查找包含在指定包中的全部的证书
*/
+ (NSSet <NSData *> *)certificatesInBundle:(NSBundle *)bundle;
/**
实例化工厂方法,默认的配置包括不容许无效或者过时证书、验证域名、不验证证书和证书中公钥
*/
+ (instancetype)defaultPolicy;
/**
指定证书验证模式的实例化工厂方法
*/
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode;
/**
指定证书验证模式和证书的实例化工厂方法
*/
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet <NSData *> *)pinnedCertificates;
/**
验证是否信任服务器
*/
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(nullable NSString *)domain;
复制代码
这个方法是将key转为NSData类型ide
#if !TARGET_OS_IOS && !TARGET_OS_WATCH && !TARGET_OS_TV
static NSData * AFSecKeyGetData(SecKeyRef key) {
CFDataRef data = NULL;
__Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out);
return (__bridge_transfer NSData *)data;
_out:
if (data) {
CFRelease(data);
}
return nil;
}
#endif
复制代码
这个方法是判断两个key是否相等post
static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
return [(__bridge id)key1 isEqual:(__bridge id)key2];
#else
return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)];
#endif
}
复制代码
这个方法是用来从证书中取出公钥学习
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;
}
复制代码
这个方法是判断服务器是否能够信任ui
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
BOOL isValid = NO;
SecTrustResultType result;
__Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
_out:
return isValid;
}
复制代码
这个方法是用来得到服务器返回的全部的证书
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];
}
复制代码
这个方法是用来得到服务器返回的全部的证书中的公钥
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];
}
复制代码
这里面频繁使用了两个宏:__Require_Quiet
和__Require_noErr_Quiet
,咱们来看一下他们的定义:
#ifndef __Require_Quiet
#define __Require_Quiet(assertion, exceptionLabel) \
do \
{ \
if ( __builtin_expect(!(assertion), 0) ) \
{ \
goto exceptionLabel; \
} \
} while ( 0 )
#endif
复制代码
能够看出__Require_Quiet
这个宏的做用是当断言为假时,跳转并执行标记以后的代码
#ifndef __Require_noErr_Quiet
#define __Require_noErr_Quiet(errorCode, exceptionLabel) \
do \
{ \
if ( __builtin_expect(0 != (errorCode), 0) ) \
{ \
goto exceptionLabel; \
} \
} while ( 0 )
#endif
复制代码
能够看出__Require_noErr_Quiet
这个宏的做用是当断言为真时,跳转并执行标记以后的代码
/**
保存服务器证书验证模式
*/
@property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
/**
保存证书中的公钥的集合
*/
@property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys;
复制代码
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
// 获取包中全部后缀为.cer的文件路径
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
// 遍历文件路径并将其对应的文件转换为NSData类型数据
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}
// 返回转换为二进制的证书的集合
return [NSSet setWithSet:certificates];
}
+ (NSSet *)defaultPinnedCertificates {
// 获取默认证书列表
static NSSet *_defaultPinnedCertificates = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
_defaultPinnedCertificates = [self certificatesInBundle:bundle];
});
return _defaultPinnedCertificates;
}
+ (instancetype)defaultPolicy {
// 按照默认实例化对象
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
return securityPolicy;
}
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
// 调用全能实例化方法
return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
}
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
// 实例化对象并设置属性
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = pinningMode;
[securityPolicy setPinnedCertificates:pinnedCertificates];
return securityPolicy;
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
// 验证证书中的域名
self.validatesDomainName = YES;
return self;
}
- (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;
}
}
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
// 当使用自建证书验证域名时,必须使用AFSSLPinningModePublicKey或者AFSSLPinningModeCertificate进行验证
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()];
}
// 添加安全策略,为serverTrust设置验证策略,即告诉客户端如何验证serverTrust
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
// 若是验证策略为不验证
if (self.SSLPinningMode == AFSSLPinningModeNone) {
// 若是信任无效或者过时证书则直接经过验证,不然根绝验证证书状况返回
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
// 若是验证策略为验证,但未验证经过而且不信任无效或者过时证书,返回未经过
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
/**
当代码走到这儿的时候表明:
1.有验证策略
2.证书验证经过
3.不信任无效或者过时证书
*/
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)];
}
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 is the Root CA)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
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;
}
复制代码
+ (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys {
return [NSSet setWithObject:@"pinnedCertificates"];
}
复制代码
添加依赖键,使pinnedPublicKeys
属性依赖于pinnedCertificates
属性,当pinnedCertificates
属性值发生变化时,pinnedPublicKeys
的观察者也能获得通知
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [self init];
if (!self) {
return nil;
}
self.SSLPinningMode = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(SSLPinningMode))] unsignedIntegerValue];
self.allowInvalidCertificates = [decoder decodeBoolForKey:NSStringFromSelector(@selector(allowInvalidCertificates))];
self.validatesDomainName = [decoder decodeBoolForKey:NSStringFromSelector(@selector(validatesDomainName))];
self.pinnedCertificates = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(pinnedCertificates))];
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:[NSNumber numberWithUnsignedInteger:self.SSLPinningMode] forKey:NSStringFromSelector(@selector(SSLPinningMode))];
[coder encodeBool:self.allowInvalidCertificates forKey:NSStringFromSelector(@selector(allowInvalidCertificates))];
[coder encodeBool:self.validatesDomainName forKey:NSStringFromSelector(@selector(validatesDomainName))];
[coder encodeObject:self.pinnedCertificates forKey:NSStringFromSelector(@selector(pinnedCertificates))];
}
复制代码
关于NSSecureCoding
在AFNetworking源码阅读(二)——从AFURLRequestSerialization入手的"4.1.2.4.7 NSSecureCoding协议方法的实现"中已经解释过了,就再也不复述
- (instancetype)copyWithZone:(NSZone *)zone {
AFSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init];
securityPolicy.SSLPinningMode = self.SSLPinningMode;
securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates;
securityPolicy.validatesDomainName = self.validatesDomainName;
securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone];
return securityPolicy;
}
复制代码
由于对HTTP和SSL不是很了解,因此只作了一些大概功能的阅读。想详细了解的话仍是得先认真学习HTTP和SSL相关知识。
源码阅读系列:AFNetworking
源码阅读:AFNetworking(二)——AFURLRequestSerialization
源码阅读:AFNetworking(三)——AFURLResponseSerialization
源码阅读:AFNetworking(四)——AFSecurityPolicy
源码阅读:AFNetworking(五)——AFNetworkReachabilityManager
源码阅读:AFNetworking(六)——AFURLSessionManager
源码阅读:AFNetworking(七)——AFHTTPSessionManager
源码阅读:AFNetworking(八)——AFAutoPurgingImageCache
源码阅读:AFNetworking(九)——AFImageDownloader
源码阅读:AFNetworking(十)——AFNetworkActivityIndicatorManager
源码阅读:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking
源码阅读:AFNetworking(十二)——UIButton+AFNetworking
源码阅读:AFNetworking(十三)——UIImageView+AFNetworking
源码阅读:AFNetworking(十四)——UIProgressView+AFNetworking