• 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

AFSecurityPolicy 之 evaluateServerTrust

网络通信/流媒体 弦苦 2561次浏览 0个评论

App Transport Security(ATS)

iOS9 引入了新特性 App Transport Security (ATS),要求 App 内访问的网络必须使用 HTTPs 协议。

在 WWDC 2016 开发者大会上,苹果宣布了一个最后期限:到 2017年1月1日 App Store 中的所有应用都必须启用 App Transport Security 安全功能

ATS 要求后台服务器必须支持最新的 TLS_V1.2 协议和 ECDH 加密算法。鉴于 HTTP 全面改造升级 HTTPs 的业务铺设尚需时日,苹果延迟了 App 强制接入 ATS 的 deadline。目前允许开发者设置 NSAllowsArbitraryLoads=YES 来暂时关闭 ATS,从而继续使用 HTTP 连接。

  1. Info.plist 中添加 App Transport Security Settings (或 NSAppTransportSecurity), 类型为 Dictionary。
  2. 在 App Transport Security Settings 下添加 Allow Arbitrary Loads (或 NSAllowsArbitraryLoads),类型为 Boolean,值设为 YES。

AFSecurityPolicy 之 evaluateServerTrust

How to migrate to HTTPS using App Transport Security when developing iOS apps
Transport security has blocked a cleartext HTTP
ATS 对 HTTP 协议屏蔽引起的问题
iOS9 & iOS10 HTTP 不能正常使用的解决办法
IOS-关于App Transport Security相关说明及适配
关于iOS9中的App Transport Security相关说明及适配
Xcode7.2与iOS9之坑 iOS 9之适配ATS

使用更安全的 HTTPs 替代 HTTP 是大势所趋,作为程序开发者必须了解 HTTPs 原理机制和 ATS 适配开发要点。
本篇续接 TLS Handshake Flow(extracts from RFCs)TLS握手协商流程解析,介绍实际访问 HTTPs 服务器的 TLS(SSL) 握手阶段需要客户端处理的 NSURLAuthenticationChallenge。

NSURLAuthenticationChallenge.NSURLProtectionSpace

iOS 中的 NSURLAuthenticationChallenge 是认证挑战类,也就是要求客户端进行挑战。
而服务器要求的挑战方式则由 NSURLAuthenticationChallenge 的属性 NSURLProtectionSpace *protectionSpace 来描述。
那么 NSURLProtectionSpace(保护空间)里面有哪些信息呢?可以肯定的是包括挑战的方式(401授权、客户端证书、服务端要求信任等),服务器的 URL 地址,端口号,协议等等。

以下为@interface NSURLProtectionSpace 向外暴露的主要属性:

@interface NSURLProtectionSpace : NSObject <NSSecureCoding, NSCopying>

@property (nullable, readonly, copy) NSString *realm;

@property (readonly) BOOL receivesCredentialSecurely;

@property (readonly) BOOL isProxy;

@property (readonly, copy) NSString *host;

@property (readonly) NSInteger port;

@property (nullable, readonly, copy) NSString *proxyType;

@property (nullable, readonly, copy) NSString *protocol;

@property (readonly, copy) NSString *authenticationMethod;

@end

对于 SSL(TLS HandShake),authenticationMethod 对应有2种类型:

  • NSURLAuthenticationMethodClientCertificate:SSL Client certificate,表示服务器要求客户端提供证书(收到 CertificateRequest)。
  • NSURLAuthenticationMethodServerTrustSecTrustRef validation required,表示需要评估是否信任服务器证书。

对于 NSURLAuthenticationMethodServerTrust,NSURLProtectionSpace 提供了一个 SecTrustRef 属性。

@interface NSURLProtectionSpace(NSServerTrustValidationSpace)

/*!
    @method serverTrust
    @abstract Returns a SecTrustRef which represents the state of the servers SSL transaction state
    @result A SecTrustRef from Security.framework.  (Nil if the authenticationMethod is not NSURLAuthenticationMethodServerTrust)
 */
@property (nullable, readonly) SecTrustRef serverTrust NS_AVAILABLE(10_6, 3_0);

@end

NSURLCredential

客户端若要接受挑战,则需提供挑战的凭证(用户和密码,或者提供客户端证书,或者信任服务器证书,或者代理)。
iOS 提供了一个 NSURLCredential 的类来表示挑战凭证。

针对 HTTP 401 错误 - 未授权: (Unauthorized),需要通过用户账号密码建立凭证:

// Initialize a NSURLCredential with a user and password
+[NSURLCredential(NSInternetPassword) credentialWithUser:password:persistence:]

针对 TLS HandShake 中服务器下发的 Certificate,macOS/iOS 的 Security.frameworkSecPolicy.h/SecTrust.h 中定义了证书校验信任评估的接口。

// Evaluates a trust reference synchronously.
OSStatus SecTrustEvaluate(SecTrustRef trust, SecTrustResultType * __nullable result);

调用 SecTrustEvaluate 对证书校验通过后,客户端需要基于对服务器的信任来建立凭证。

// Create a new NSURLCredential which specifies that a handshake has been trusted. +[NSURLCredential(NSServerTrust) credentialForTrust:];

Apple Developer Guide for Security

Security Starting Point
Secure Coding Guide
Certificate, Key, and Trust Services

Cryptographic Services Guide
Authentication, Authorization, and Permissions Guide
Certificate, Key, and Trust Services Programming Guide

Security.SecureTransport
Using the Secure Socket Layer for Network Communication

HTTPS Server Trust Evaluation
Overriding TLS Chain Validation Correctly
Authentication Challenges and TLS Chain Validation

Sample Code

AdvancedURLConnections

Networking, Internet, & Web | Protocol Streams | Sample Code

This sample demonstrates various advanced networking techniques with NSURLConnection. Specifically, it demonstrates how to respond to authentication challenges, how to modify the default server trust evaluation (for example, to support a server with a self-signed certificate), and how to provide client identities.

CustomHTTPProtocol

Core Services Layer | Foundation | Sample Code

CustomHTTPProtocol shows how to use an NSURLProtocol subclass to intercept the NSURLConnections made by a high-level subsystem that does not otherwise expose its network connections. In this specific case, it intercepts the HTTPS requests made by a web view and overrides server trust evaluation, allowing you to browse a site whose certificate is not trusted by default.

AFSecurityPolicy

当基于 NSURLConnection 或 NSURLSession 访问 HTTPs 网站时,NSURLConnection 或 NSURLSession 并没有验证证书是否合法,无法避免中间人攻击。
要做到真正的安全通信,需要客户端校验确认是否信任服务器证书(链)并提供凭证。否则会返回以下错误:

NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9843)

AFNetworking 的 AFSecurityPolicy 接口封装了证书验证的过程。

收到 NSURLAuthenticationChallenge

在收到 Certificate 后,相关回调 NSURLConnectionDelegate/NSURLSessionDelegate 中调用 AFSecurityPolicy 对 NSURLAuthenticationChallenge 进行处理。

AFNetworking 2.x 的 AFURLConnectionOperation/NSURLConnection
回调 NSURLConnectionDelegate
-[AFURLConnectionOperation connection:willSendRequestForAuthenticationChallenge:]

AFNetworking 3.x 的 AFURLSessionManager 的回调 NSURLSessionDelegate
-[AFURLSessionManager URLSession:didReceiveChallenge:completionHandler:]
NSURLSessionTaskDelegate
-[AFURLSessionManager URLSession:task:didReceiveChallenge:completionHandler:]

接受 NSURLAuthenticationChallenge

willSendRequestForAuthenticationChallengedidReceiveChallenge 中均需要处理如何接受挑战(NSURLAuthenticationChallenge)。

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        // 校验评估是否信任证书(链)
        if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
            // 创建 NSURLCredential 对象
            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            // NSURLConnectionDelegate/willSendRequestForAuthenticationChallenge:为 challenge 的发送方提供 credential
            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
            // NSURLSessionDelegate/didReceiveChallenge:回调 completionHandler
            // completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
        } else {
            // NSURLConnectionDelegate/willSendRequestForAuthenticationChallenge
            [[challenge sender] cancelAuthenticationChallenge:challenge];
            // NSURLSessionDelegate/didReceiveChallenge:回调 completionHandler
            // completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
        }
    }

evaluateServerTrust forDomain

-[AFSecurityPolicy evaluateServerTrust:forDomain:] 主要调用了 SecPolicyCreateSSLSecTrustSetPoliciesSecTrustEvaluate 这3个函数。

-[AFSecurityPolicy evaluateServerTrust:forDomain:]
{
    // 1. 为服务器 domain 创建 SecPolicyRef
    [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];

    // 2. 为 SecTrustRef 设置 SecPolicyRef[]
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);

    // 3.调用 SecTrustEvaluate 校验证书(链)——SecTrustRef
    if (self.SSLPinningMode == AFSSLPinningModeNone) {
        return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
    }

}

challenge.protectionSpace.serverTrust 为证书,challenge.protectionSpace.host(或调用 [[connection currentRequest] valueForHTTPHeaderField:@”host”] 获取)为服务器域名。
以上展示的是 AFSSLPinningModeNone 模式完全信任服务器(domain)证书的基本处理流程。

SSL Pinning

除了去系统信任 CA 机构列表验证的 AFSSLPinningModeNone 模式,AFNetworking 还提供了 SSL Pinning 方式的验证。

SSL Pinning 方式(AFSSLPinningModeCertificateAFSSLPinningModePublicKey)把服务端下发的证书预先打包到 APP 的 bundle 中,然后通过比较服务端下发的证书和本地证书是否相同来校验证书。
使用该方式的原因是CA机构颁发的证书比较昂贵,一些企业或者个人不申请CA颁发的证书,而是自己手动创建证书。用 SSL Pinning 的方式只要比较证书内容一样,无需验证证书的权威性。

  • AFSSLPinningModeNone

    这个模式表示不做 SSL pinning,只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书(链)。
    若证书是信任机构签发的就会通过;若是自己服务器生成的证书,这里是不会通过的。

  • AFSSLPinningModeCertificate

    这个模式表示用证书绑定方式验证证书,需要客户端保存服务端的证书拷贝。
    适用于非浏览器应用,因为浏览器跟很多未知服务端打交道,无法把每个服务端的证书都保存到本地。但CS架构的APP应用一般事先知道要进行通信的服务端(例如 QQ 文件后台服务器:*.ftn.qq.com),可以直接在客户端保存这些固定服务端的证书用于校验。

    验证分两步:第一步验证证书的域名/有效期等信息;第二步是对比服务端返回的证书跟客户端返回的是否一致。
    从代码上看,和去系统信任机构列表里验证一样调用 SecTrustEvaluate,只是这里的列表换成了客户端预先保存的证书列表(链)。

  • AFSSLPinningModePublicKey

    这个模式同样是用证书绑定方式验证,客户端要有服务端的证书拷贝,只是验证时只验证证书里的公钥,不验证证书的有效期等信息。
    只要公钥是正确的,就能保证通信不会被窃听,因为中间人没有私钥,无法解开通过公钥加密的数据。

AFSSLPinningModeCertificate

AFCertificateTrustChainForServerTrust 中调用 SecTrustGetCertificateCount(serverTrust) 取得 SecTrustRef 证书链的长度;通过 SecTrustGetCertificateAtIndex 获取到证书链(trustChain)上的每一个证书(SecCertificateRef)。
然后对 trustChain(serverCertificates) 进行验证,具体参考源码注释。

AFSSLPinningModePublicKey

AFPublicKeyTrustChainForServerTrust 中获取证书链(trustChain)上的每一个证书(SecCertificateRef)后, 调用 SecTrustCreateWithCertificates 获取每个证书(SecCertificateRef)对应的 SecTrustRef,进而调用 SecTrustEvaluate 校验。
校验通过后,调用 SecTrustCopyPublicKey 获取每个证书 SecTrustRef 对应的公钥(SecKeyRef)。
然后对 trustChain(publicKeys)进行校验,具体参考源码注释。

源代码

以下摘自最新 AFNetworking 3.x 源码,稍作注释:

-[AFSecurityPolicy evaluateServerTrust:forDomain:]
{

    // 4.从 SecTrustRef 获取 trustChain,进行 SSL Pinning 验证。

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

            // The SecTrustEvaluate function looks for an anchor certificate in the array of certificates specified by the SecTrustSetAnchorCertificates function
            SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
            // 指定在本地证书列表(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);
            // 反向遍历服务器下发的证书链(serverCertificates),一般如果本地预存证书包含服务器返回的证书(或证书链的根证书),则校验通过。
            for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
                if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                    return YES;
                }
            }

            return NO;
        }
        case AFSSLPinningModePublicKey: {
            NSUInteger trustedPublicKeyCount = 0;
            // 获取服务器下发的证书链(serverCertificates)对应的公钥
            NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);

            // 遍历对比服务器下发证书公钥和本地预存证书公钥(pinnedPublicKeys)
            for (id trustChainPublicKey in publicKeys) {
                for (id pinnedPublicKey in self.pinnedPublicKeys) {
                    if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                        trustedPublicKeyCount += 1;
                    }
                }
            }

            // AFPublicKeyTrustChainForServerTrust 并没有严格要求证书链都通过 SecTrustEvaluate,这里匹配上一个即算验证通过。
            return trustedPublicKeyCount > 0;
        }
    }

}

参考

HTTP协议授权访问
https信任证书的三种方案

AFNetworking 2.3.1 源码解析之 三. AFSecurityPolicy
AFNetworking 2.6.2 源码解析之 AFSecurityPolicy
通读AFN ③–HTTPS访问控制(AFSecurityPolicy)
AFNetworking 源码分析之 (五)验证 HTTPS 请求的证书
AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy

iOS https自建证书 请求服务器 和 WKWebView


开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明AFSecurityPolicy 之 evaluateServerTrust
喜欢 (0)

您必须 登录 才能发表评论!

加载中……