AFNetworking源码学习之二-HTTPS请求

2017-10-11  本文已影响0人  会笑的Even

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开启,拒绝这个请求.

上一篇下一篇

猜你喜欢

热点阅读