简单来说,AFSecurityPolicy的作用就是证书认证。
在https请求中,客户端会验证服务端的证书,但是这个过程也可能会受到中间人攻击,就像Charles或者Fiddler等工具抓包,为了防止受到它们的伪证书欺骗,这个时候证书锁定就有用了,将SSL证书内置到我们的应用里面,与服务端返回的证书进行校验。这也是AFSecurityPolicy的主要工作。
获取工程里面的.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];
}
+ (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;
}
实例化方法里面主要是设置了验证模式和证书
AFSSLPinningMode有三种模式
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone,//不进行本地验证
AFSSLPinningModePublicKey,//只验证服务器返回的证书里的公钥
AFSSLPinningModeCertificate,//验证本地证书和服务器返回证书
};
证书设置方法,并且将证书的公钥保存到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;
}
}
接下来就是验证服务端证书是否有效的方法
- (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];
//生成验证策略。如果要验证域名,就以域名为参数创建一个策略,否则创建默认的basicX509策略
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);
//如果是AFSSLPinningModeNone(不做本地证书验证,从客户端系统中的受信任颁发机构 CA 列表中去验证服务端返回的证书)
if (self.SSLPinningMode == AFSSLPinningModeNone) {
//不使用ssl pinning 但允许自建证书,直接返回YES;否则进行第二个条件判断,去客户端系统根证书里找是否有匹配的证书,验证serverTrust是否可信,直接返回YES
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { //如果验证无效AFServerTrustIsValid,而且allowInvalidCertificates不允许自签,返回NO
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
//把证书data,用系统api转成 SecCertificateRef 类型的数据,SecCertificateCreateWithData函数对原先的pinnedCertificates做一些处理,保证返回的证书都是DER编码的X.509证书
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
// 将pinnedCertificates设置成需要参与验证的Anchor Certificate(锚点证书,通过SecTrustSetAnchorCertificates设置了参与校验锚点证书之后,假如验证的数字证书是这个锚点证书的子节点,即验证的数字证书是由锚点证书对应CA或子CA签发的,或是该证书本身,则信任该证书),具体就是调用SecTrustEvaluate来验证
//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);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
//如果我们的证书中,有一个和它证书链中的证书匹配的,就返回YES
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;
}
那么问题来了,
什么是SecTrustRef?
SecTrustRef:其实就是一个容器,装了服务器端需要验证的证书的基本信息、公钥等等,不仅如此,它还可以装一些评估策略,还有客户端的锚点证书,这个客户端的证书,可以用来和服务端的证书去匹配验证的。
那么什么又是锚点证书?
锚点证书通常指:嵌入到操作系统中的根证书(权威证书颁发机构颁发的自签名证书),CA 的证书链的锚点证书已经在系统中了,自签证书需要通过SecTrustSetAnchorCertificates方法设置为锚点证书,不然验证不会通过
X.509证书???
X.509是密码学里公钥证书的格式标准。X.509证书里含有公钥、身份信息和签名信息。HTTPS依赖的SSL证书使用的就是使用的X.509格式
网友评论