优质广告供应商

广告是为了更好地支持作者创作

AFSecurityPolicy 安全策略

https 的基础内容,提前预习

AFSecurityPolicy 注释

PS: 小结

  1. AFSSLPinningModeCertificate :证书模式 1)判断信任链上的公钥是否有交集 2)判断授权是否有效等信息
  2. AFSSLPinningModePublicKey : 公钥模式:只是 判断信任链上的公钥是否有交集
// 这个方法就是简单的判断两个对象是否相等
static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
    return [(__bridge id)key1 isEqual:(__bridge id)key2];
}

通过证书数据获取公钥

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(); // X509的格式策略
    __Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
 // 证书数据、策略 创建一个信任对象

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); 
// 对信任对象进行评估, 是否是信任的,如果信任的
#pragma clang diagnostic pop

// 如果信任的就获取公钥返回
    allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); 

_out:
    if (allowedTrust) {
        CFRelease(allowedTrust);
    }

    if (policy) {
        CFRelease(policy);
    }

    if (allowedCertificate) {
        CFRelease(allowedCertificate);
    }

    return allowedPublicKey;
}
//服务器信任是否有效
 // 传入的是服务器的信任对象serverTrust,, 其实就是证书的信任对象
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
    BOOL isValid = NO;
    SecTrustResultType result;
    NSError *error;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
//    检测信任对象的结果
#pragma clang diagnostic pop

    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(); // X509格式的安全策略
    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;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
        __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
#pragma clang diagnostic pop
        [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)]; 

    _out:
        if (trust) {
            CFRelease(trust);
        }

        if (certificates) {
            CFRelease(certificates);
        }

        continue;
    }
    CFRelease(policy);

    return [NSArray arrayWithArray:trustChain];
}


AFSecurityPolicy 对象


 // 获取bundle下面的证书
+ (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];
}

// 默认安全策略
+ (instancetype)defaultPolicy {
    AFSecurityPolicy *securityPolicy = [[self alloc] init];
    securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
    return securityPolicy;
}

// 定位模式的策略
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
    NSSet <NSData *> *defaultPinnedCertificates = [self certificatesInBundle:[NSBundle mainBundle]]; // 证书的数据
    return [self policyWithPinningMode:pinningMode withPinnedCertificates: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;
    }
}

*重点方法*

// serverTrust 服务器的信任对象
// domain : 域名
// 遍历从当前的所有证书中获取证书
- (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)];
      // 域名有效, 通过域名创建SSL验证
    } else {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];   //  X509 策略
    }

    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); // 设置信任对象

    if (self.SSLPinningMode == AFSSLPinningModeNone) {
        // 这个服务器判断有效
        return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
    } else if (!self.allowInvalidCertificates && !AFServerTrustIsValid(serverTrust)) {
        return NO;
    }

    switch (self.SSLPinningMode) {
        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;
        }
        default:
            return NO;
    }
    return NO;
}

NSCoding与NSSecureCoding相比于NSSecureCoding 更安全。防止替换攻击,保证你读到的数据正是你写入的数据

优质广告供应商

广告是为了更好地支持作者创作

推荐阅读更多精彩内容

  • 注:本文始发于个人 GitHub 项目 ShannonChenCHN/iOSDevLevelingUp[https...
    ShannonChenCHN阅读 1,764评论 0 25
  • Change Log All notable changes to this project will be do...
    SmallTwo阅读 444评论 0 1
  • 优质广告供应商

    广告是为了更好地支持作者创作

  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 13,337评论 1 178
  • Reference: https://www.cainwang.cn/afnssl/  AFNetworking,...
    圣迪阅读 36,917评论 57 175
  • 内容包括:框架、组件、测试、Apple Store、SDK、XCode、网站、书籍等。Swift 语言写成的项目会...
    iOS_Alex阅读 15,892评论 14 260
  • 👏欢迎前往本人的GitHub查看更多内容。[点击前往GitHub](https://github.com/iOSh...
    iOS超级洋阅读 259评论 0 1
  • 优质广告供应商

    广告是为了更好地支持作者创作

  • 此系列文章均整理、精简自pluto-y大神的博客,感谢大神~ 一、库分类公有库:公开给所有人的库私有库:不公开给所...
    Azen阅读 1,160评论 0 1
  • UIKit 1.UIView 和 CALayer 是什么关系? UIView 继承 UIResponder,而 U...
    Sephiroth_Ma阅读 1,700评论 0 24
  • AFNetworking:转自 AFNetWorking原理 首先我们先看原生的网络请求过程: NSMutable...
    飞哥漂流记阅读 467评论 0 1
  • Substrate的transaction-payment模块分析 transaction-payment模块提供...
    建怀阅读 7,436评论 0 4
  • 优质广告供应商

    广告是为了更好地支持作者创作

  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 5,302评论 0 4
  • 公元:2019年11月28日19时42分农历:二零一九年 十一月 初三日 戌时干支:己亥乙亥己巳甲戌当月节气:立冬...
    石放阅读 6,360评论 0 2
  • 今天上午陪老妈看病,下午健身房跑步,晚上想想今天还没有断舍离,马上做,衣架和旁边的的布衣架,一看乱乱,又想想自己是...
    影子3623253阅读 2,522评论 2 8
  • 年纪越大,人的反应就越迟钝,脑子就越不好使,计划稍有变化,就容易手忙脚乱,乱了方寸。 “玩坏了”也是如此,不但会乱...
    玩坏了阅读 1,813评论 2 1
  • 优质广告供应商

    广告是为了更好地支持作者创作

  • 感动 我在你的眼里的样子,就是你的样子。 相互内化 没有绝对的善恶 有因必有果 当你以自己的价值观幸福感去要求其他...
    周粥粥叭阅读 1,463评论 1 5
  • 想要快速入门CAD,对于零基础的新手来说的确有一定的困难。不过只要你掌握了以下这些CAD快速入门技巧,你就跨进了C...
    努力的王榆阅读 2,617评论 0 3
  • 昨天考过了阿里规范,心里舒坦了好多,敲代码也犹如神助。早早完成工作回家喽
    常亚星阅读 2,724评论 0 1
  • 三军可夺气,将军可夺心。是故朝气锐,昼气惰,暮气归。善用兵者,避其锐气,击其惰归,此治气者也。以治待乱,以静待哗,...
    生姜牛奶泡腾片阅读 1,417评论 0 1