AFNetworking 框架小结 六 (AFSecurityPolicy)(转)

原创作者: 那夜的星空分外清澈
版权声明: 本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011374318/article/details/79302085

在 AFNetworking 框架中,可以使用 AFSecurityPolicy 类来设置 SSL 安全连接时的校验策略。在客户端应用中添加遵循 X.509 标准的数字证书,
并在与服务器建立安全连接时校验服务器传递的安全信息,这种方式可以有效避免中间人攻击等风险。

AFSecurityPolicy 的校验选项 AFSSLPinningMode 有三种:

AFSSLPinningModeNone 代表客户端无条件地信任服务器端返回的证书。(默认)
AFSSLPinningModePublicKey 代表客户端会将服务器端返回的证书与本地保存的证书中的PublicKey部分进行校验;如果正确,才继续进行。
AFSSLPinningModeCertificate 代表客户端会将服务器端返回的证书和本地保存的证书中的所有内容,包括PublicKey和证书部分,全部进行校验;如果正确,才继续进行

属性

SSLPinningMode                 AFSSLPinningMode 校验策略
pinnedCertificates             NSSet <NSData *> 本地证书
pinnedPublicKeys               NSSet            本地证书所包含的公钥
allowInvalidCertificates       BOOL             是否信任证书是无效或过期的服务器,默认值为 NO
validatesDomainName            BOOL             是否校验证书中 CN 字段中的域名,默认值为 YES

方法

创建默认的安全策略
+ (instancetype)defaultPolicy;
在这个方法中,将 SSLPinningMode 的值设置为 AFSSLPinningModeNone 。
创建指定校验模式的安全策略
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
    return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
}
这个类的方法,实际是调用了下面的方法返回一个策略,只是其所提供参数,表示的是默认当前类名包下的所有证书, 
如果没有名为 AFSecurityPolicy 的包,那么则没有本地证书以供校验时使用。
创建指定校验模式和证书的安全策略
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates;
这个方法,完全由调用者自己提供校验选项和证书数据。
获取指定包中的证书数据
+ (NSSet <NSData *> *)certificatesInBundle:(NSBundle *)bundle; 
该方法可以很方便的获取指定包 bundle 中的证书数据,结合上一个方法,则创建安全策略实例对象十分方便。
校验服务器传递的安全信息
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(nullable NSString *)domain; 
这个方法会根据当前策略来判断接收的服务器的安全信息是否有效,域名参数 domain 如果未传递,则不对其进行校验。如果要校验自签名证书的域名,那么本地应包含该证书并且校验策略不能是 AFSSLPinningModeNone 选项。
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
    [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
    [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);

从上面的源代码可知,如果不对域名进行校验,便会创建 X.509 标准的默认校验策略,而后,将策略与服务器传递的安全信息 serverTrust 相关联。

接着执行下面的代码:

if (self.SSLPinningMode == AFSSLPinningModeNone) {
    return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
    return NO;
}

这里有点绕,如果策略选项为 AFSSLPinningModeNone ,那么分了两种情况,一种是,若 allowInvalidCertificates 属性值为 YES ,
即客户端信任无效或过期的证书(可能是自签名证书),那么,客户端则认为服务器是可信的。
另一种,则是调用 AFServerTrustIsValid 函数校验服务器传递的安全信息 serverTrust 是否是有效的。

如果策略选项不是 AFSSLPinningModeNone 并且 AFServerTrustIsValid 校验未通过,而客户端也不信任无效或过期的证书,那么则认为所连接的服务器不可信。

再接着往下校验,则是对 AFSSLPinningModeCertificate 和 AFSSLPinningModePublicKey 选项的分别处理,前者比较的是安全信息中的证书和本地的证书是否相同,
而后者则是对证书中的公钥进行比较。

在 AFSSLPinningModeCertificate 选项下,先调用 SecCertificateCreateWithData 函数将 pinnedCertificates 属性中的所有的本地证书数据转化为证书变量,
而后使用 SecTrustSetAnchorCertificates 函数将这些变量设置为校验服务器安全信息 serverTrust 的锚证书。

当确定 serverTrust 信息本身是有效的后,再调用 AFCertificateTrustChainForServerTrust 函数获取其所包含的所有证书 serverCertificates ,这个证书数组,第一个为子证书,最后一个可能是根证书(当然也可能不是),这样,再判断本地的所有证书 pinnedCertificates 中是否包含了 serverCertificates 中的一个证书。判断过程是从数组最后一个元素开始的,即从父证书开始判断。只要包含一个证书,那么就认为所连接的服务器是可以信任的。

在 AFSSLPinningModePublicKey 选项下,先调用 AFPublicKeyTrustChainForServerTrust 函数获取 serverTrust 中所含证书的所有公钥,而后,遍历这些公钥,如果有一个公钥在 pinnedPublicKeys 属性中的所有公钥中,那么就认为服务器是可信的。

具体的源代码如下:

- (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()];
    }

    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);

    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)];
            }
            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]) {
                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;
}

作者:那夜的星空分外清澈
来源:CSDN
原文:https://blog.csdn.net/u011374318/article/details/79364995
版权声明:本文为博主原创文章,转载请附上博文链接!

🌰:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

NSString *cerPath                = [[NSBundle mainBundle] pathForResource:@"https" ofType:@"cer"];
NSData *certData                 = [NSData dataWithContentsOfFile:cerPath];
AFSecurityPolicy *securityPolicy = [[AFSecurityPolicy alloc] init];
[securityPolicy setAllowInvalidCertificates:NO];
[securityPolicy setPinnedCertificates:@[certData]];
[securityPolicy setSSLPinningMode:AFSSLPinningModeCertificate];
[securityPolicy setValidatesDomainName:YES];

manager.securityPolicy = securityPolicy;
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; 

AFSecurityPolicy *securityPolicy = [[AFSecurityPolicy alloc] init]; 
[securityPolicy setAllowInvalidCertificates:NO]; 
[securityPolicy setSSLPinningMode:AFSSLPinningModeCertificate]; 
[securityPolicy setValidatesDomainName:YES];
[securityPolicy setValidatesCertificateChain:NO]; 

manager.securityPolicy = securityPolicy;

🌰来源:https://www.jianshu.com/p/4102b817ff2f

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容