【腾讯Bugly干货分享】iOS 中 HTTPS 证书验证浅析 (3)

表示验证结果。其中 kSecTrustResultProceed表示serverTrust验证成功,且该验证得到了用户认可(例如在弹出的是否信任的alert框中选择always trust)。 kSecTrustResultUnspecified表示 serverTrust验证成功,此证书也被暗中信任了,但是用户并没有显示地决定信任该证书。 两者取其一就可以认为对serverTrust验证成功。

SecTrustEvaluate

函数内部递归地从叶节点证书到根证书验证。使用系统默认的验证方式验证Trust Object,根据上述证书链的验证可知,系统会根据Trust Object的验证策略,一级一级往上,验证证书链上每一级证书有效性。

NSURLCredential

表示身份验证证书。URL Lodaing支持3种类型证书:password-based user credentials, certificate-based user credentials, 和certificate-based server credentials(需要验证服务器身份时使用)。因此NSURLCredential可以表示由用户名/密码组合、客户端证书及服务器信任创建的认证信息,适合大部分的认证请求。对于NSURLCredential也存在三种持久化机制:

NSURLCredentialPersistenceNone :要求 URL 载入系统 “在用完相应的认证信息后立刻丢弃”。

NSURLCredentialPersistenceForSession :要求 URL 载入系统 “在应用终止时,丢弃相应的 credential ”。

NSURLCredentialPersistencePermanent :要求 URL 载入系统 “将相应的认证信息存入钥匙串(keychain),以便其他应用也能使用。

对于已经验证通过的信任对象,客户端也可以不提供证书凭证。

对于NSURLSession,传递如下之一的值给completion handler回调:

NSURLSessionAuthChallengePerformDefaultHandling处理请求,就好像代理没有提供一个代理方法来处理认证请求

NSURLSessionAuthChallengeRejectProtectionSpace拒接认证请求。基于服务器响应的认证类型,URL加载类可能会多次调用代理方法。

对于 NSURLConnection 和 NSURLDownload,在[challenge sender] 上调用continueWithoutCredentialsForAuthenticationChallenge:方法。不提供证书的话,可能会导致连接失败,调用connectionDidFailWithError:方法 ,或者会返回一个不需要验证身份的替代的URL。 如下代码:

【腾讯Bugly干货分享】iOS 中 HTTPS 证书验证浅析

对于非自签名的证书,即使服务器返回的证书是信任的CA颁发的,而为了确定返回的证书正是客户端需要的证书,这需要本地导入证书,并将证书设置成需要参与验证的锚点证书,再调用SecTrustEvaluate通过本地导入的证书来验证服务器证书是否是可信的。如果服务器证书是这个锚点证书对应CA或者子CA颁发的,或服务器证书本身就是这个锚点证书,则证书信任通过。如下代码(参考文档):

【腾讯Bugly干货分享】iOS 中 HTTPS 证书验证浅析

自签名证书验证实现

对于自签名证书,这样Trust Object中的服务器证书是不可信任的CA颁发的,直接使用SecTrustEvaluate验证是不会成功的。可以采取下述简单代码绕过HTTPS的验证:

【腾讯Bugly干货分享】iOS 中 HTTPS 证书验证浅析

上述代码一般用于当服务器使用自签名证书时,为了方便测试,客户端可以通过该方法信任所有自签名证书。

综上对非自建和自建证书验证过程的分析,可以总结如下:

获取需要验证的信任对象(Trust Object)。对于NSURLConnection来说,
是从delegate方法-connection: willSendRequestForAuthenticationChallenge:回调回来的参数challenge中获取(challenge.protectionSpace.serverTrust) 。

使用系统默认验证方式验证Trust Object。SecTrustEvaluate会根据Trust Object的验证策略,一级一级往上,验证证书链上每一级数字签名的有效性,从而评估证书的有效性。

如第二步验证通过了,一般的安全要求下,就可以直接验证通过,进入到下一步:使用Trust Object生成一份凭证([NSURLCredential credentialForTrust:serverTrust]),传入challenge的sender中([challenge.sender useCredential:cred forAuthenticationChallenge:challenge])处理,建立连接。

假如有更强的安全要求,可以继续对Trust Object进行更严格的验证。常用的方式是在本地导入证书,验证Trust Object与导入的证书是否匹配。

假如验证失败,取消此次Challenge-Response Authentication验证流程,拒绝连接请求。

假如是自建证书的,则不使用第二步系统默认的验证方式,因为自建证书的根CA的数字签名未在操作系统的信任列表中。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzspjy.html