在APP内做HTTPS请求后,使用NSURLConnection
或NSURLSession
都会接收到信任相关的方法回调,做进一步的处理。
因为在https的通信过程里,请求方需要使用对方证书生成一个随机的加密秘钥,用来做之后请求数据的加密。核心问题就是对方的证书是不是可信任的,所以就有了上面iOS请求回调的方法。
//NSURLSession的回调方法
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;
这个方法结束内部最后需要调用参数里的completionHandler
,把证书和处置方式传递回去。
而NSURLConnection
稍微不同,它的委托回调里,实现- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
方法,然后在方法里调用5种处理方法之一:
In this method,you must invoke one of the challenge-responder methods (NSURLAuthenticationChallengeSender
protocol):
关于证书,一种是从机构签发授权的CA证书,一种是自己生成的证书。证书有一个授权链,A授权B,B授权C,然后有一个是根证书。在操作系统里会有受信任的根证书列表,iOS系统的可以查看iOS 中可用的受信任根证书列表。如果根证书是信任的,那么它的授权链里所有证书都通过验证。所以对于CA证书,如果它的根证书是操作系统本身信任的,那么就可以让系统做验证。
用电脑浏览器做请求的时候可以直观的看到,用chrome打开HTTPS网站,然后开发者工具查看security,点击view certificate,就可以看到证书的授权链:
在钥匙串里的系统根证书就可以看到操作系统信任的根证书。
顺带看了下12306的问题,用的是自己做的根证书Sinorail Certification Authority,不是系统默认信任的那些机构,所以第一次打开总会看到浏览器的警告。浏览器为什么警告?为了提醒用户,因为有可能这是一个钓鱼网站+假的ssl证书。而现在一般的情况是大家都养成了继续请求、忽略警告的习惯。
OSStatus SecTrustEvaluate(SecTrustRef trust, SecTrustResultType * __nullable result)
是检测的核心方法,会从当前证书沿着授权链验证到根证书。
而如果是自己建的ssl证书,那么就要在APP内加入和服务器相同的证书,使用证书文件来做验证,在请求HTTPS回调后,验证服务端的证书和我们本地的证书是否数据一致。考虑到授权链的问题,APP内的证书可能和服务端返回的证书是一个父子关系,所以需要把服务端的证书的授权链里所有证书都获取到后,依次和APP本地的证书做校验。
不过这样似乎有个问题,就是你的证书会暴露在你的APP文件里,如果把APP通过电脑下载下来,拿到ipa文件就可以解压拿到里面的.cer证书了!
不过刚才看了下iTunes,新版本竟然去掉了APP Store,点击应用全部都跳转到网页,而网页没有下载功能!