zoukankan      html  css  js  c++  java
  • ios AFNetworking 3.0 源码阅读分析 (三)(AFSecurityPolicy模块)

      本文主要讲解AFSecurityPolicy,它是AFNetworking安全相关的模块。当做https请求时,如果需要服务器认证就会使用到它。而在AFNetworking中只做了对服务器的认证,并没有提供对客户端认证。


      在学习 AFSecurityPolicy之前,最好要有数字证书原理,ssl,https相关知识基础。相关文章挺多,可参考文章,这篇是我看过写的最好的。接下来就回到AFSecurityPolicy模块。它提供了三种的检证模式:

    typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
        AFSSLPinningModeNone,
        AFSSLPinningModePublicKey,
        AFSSLPinningModeCertificate,
    };
    AFSSLPinningModeNone:这种验证模式下会在系统信任的证书列表里验证服务器的证书是否可信。

    AFSSLPinningModePublicKey:这种是证书绑定方式验证证书,需要客户端保存有服务端的证书拷贝,然后通过对比客户端保存的与要验证服务器的是否一致。
    AFSSLPinningModeCertificate:这种模式同样是用证书绑定方式验证,客户端要有服务端的证书拷贝,验证时只验客户端保存的与要验证服务器的公钥是否一致即可。

    对于以上三种模式的逻辑都在- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain;方法中实现了。有了上面的基础知识,相信源码应该也不难明白。

    - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                      forDomain:(NSString *)domain
    {
        //域名不为空且允许过期证书且要验证域名且(验证模式为不做SSL pinning || 本地保存证书为空)
        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是基于种安全策略验证的
        SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
    
        if (self.SSLPinningMode == AFSSLPinningModeNone) {
            //这里有个点AFServerTrustIsValid里面的SecTrustEvaluate要在子线程访问,线程不安全的
            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;
    }

    对于如何获取证书,获取公钥,这些都有系统接口,AFSecurityPolicy正是使用了这些接口去完成需要做的事情。但AFSecurityPolicy有几个地方可能会有点难理解:

    #ifndef __Require_Quiet
        #define __Require_Quiet(assertion, exceptionLabel)                            
          do                                                                          
          {                                                                           
              if ( __builtin_expect(!(assertion), 0) )                                
              {                                                                       
                  goto exceptionLabel;                                                
              }                                                                       
          } while ( 0 )
    #endif

    首先__builtin_expect宏是做什么的 可参考这一篇GCC __builtin_expect的作用

    而为什么要加上do while,那是一匹布那么长,可参考宏定义的黑魔法,看这篇真的是大开眼界。

    其它细节应该就没有么难点了。


      总结:看完这个,希望能明白有三种验证模式、AFNetworking默认使用了AFSSLPinningModeNone,这就足够了,至于更多细节都在源码中,就不在这啰嗦了。



     

       

  • 相关阅读:
    数据字典/动态性能视图
    参数管理
    expdp实现oracle远程服务器导出到本地
    jquery 操作单选按钮
    vs2012加载T4MVC模板
    Asp.net Mvc 过滤器执行顺序
    oracle版本及字符集查询
    ora-01658: 无法为表空间*****中的段创建 INITIAL 区
    SmtpClient发送邮件
    盒模型padding和margin对滚动条位置的影响
  • 原文地址:https://www.cnblogs.com/chenxianming/p/5704449.html
Copyright © 2011-2022 走看看