美文网首页
AFNetworking源码学习之二-HTTPS请求

AFNetworking源码学习之二-HTTPS请求

作者: 会笑的Even | 来源:发表于2017-10-11 11:44 被阅读0次

    HTTPS是基于HTTP和SSL的合成,在HTTP的基础上多了一步证书认证的过程,其大致步骤如下:

    HTTPS.jpg

    1.客户端向服务端发送请求.
    2.服务端返回证书包括公钥和一些基本信息.服务端配置证书可向第三方申请证书,此时不会弹出警告框,也可以自己创建证书,访问时会弹出警告框.
    3.客户端验证证书,如果证书不合法,弹出HTTPS警告,合法的话,生成一个随机数,将随机数利用公钥加密,作为对称密钥,传给服务器.
    4.服务器利用公钥解析,得到随机数,最为对称加密的密钥,将要返回的数据进行加密传输给客户端.
    5.客户端利用随机数对称密钥解密数据.

    这只是单向认证,还有双向认证.

    下面看看AFN中具体实现:

    - (void)URLSession:(NSURLSession *)session
    didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
     completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
    {   // 默认处理
        NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        __block NSURLCredential *credential = nil;
        // 自定义验证服务端挑战
        if (self.sessionDidReceiveAuthenticationChallenge) {
            disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
        } else {
            // 默认处理服务端挑战(单向认证)
            if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
                // 根据安全策略评估服务端的信任
                if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                    // 创建信任
                    credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                    if (credential) {
                        // 信任挑战
                        disposition = NSURLSessionAuthChallengeUseCredential;
                    } else {
                        // 默认处理挑战
                        disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                    }
                } else {
                    // 取消挑战
                    disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
                }
            } else {
                // 默认处理挑战
                disposition = NSURLSessionAuthChallengePerformDefaultHandling;
            }
        }
        // 根据前面的处理和信任,完成挑战
        if (completionHandler) {
            completionHandler(disposition, credential);
        }
    }
    

    这里主要做了以下几件事情:
    1.如果实现了自定义的处理挑战,就用自定的处理方式.
    2.如果没有实现自定义的处理挑战方式,就用系统的处理方式.

    根据安全策略评估服务端的信任的代码如下:

    - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                      forDomain:(NSString *)domain
    {   // 如果有域名,且允许自建证书,需要验证域名, AFSSLPinningModeNone或证书个数为0时
        if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
            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;
                }
    
                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;
    }
    

    对于我们自己做做https请求的话,如果是付费证书,我们什么也不用做,如果是自建证书需要在plist文件设置可以返回不安全的请求,然后AFN已经将要做的事情帮我们做了,我们只需写:

        // 允许自签名证书
        policy.allowInvalidCertificates = YES;
        // 可以验证域名(需要导入自签名证书)
        policy.validatesDomainName = YES;
    
            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];
           }
        policy.pinnedCertificates = certificates;
    

    AFN在系统验证之前,帮我们验证了,如果验证通过了走系统的验证,系统验证如果匹配到了证书,就返回安全的链接,如果匹配不成功,判断ATS,ATS关闭,返回不安全的链接,如果ATS开启,拒绝这个请求.

    相关文章

      网友评论

          本文标题:AFNetworking源码学习之二-HTTPS请求

          本文链接:https://www.haomeiwen.com/subject/emtbyxtx.html