iOS开发中使用的通信协议,除了聊天和物联网相关的APP可能会用到XMPP和MQTT,剩下的应用通常都只会用到HTTP和HTTPS这两种协议,而由于ATS的限制,iOS默认都是实用HTTPS,除非修改项目的info.plist:
对于HTTPS,熟悉HTTPS协议的人都知道,HTTPS基本上就是在HTTP协议的基础上加了一层SSL/TLS加密协议,这层加密协议在建立通信时除了HTTP本身的3次握手外,还会再经历额外的身份校验、数据加解密过程:
这些过程中会混合使用非对称加密、对称加密、证书校验和数字签名等手段,其中证书校验的过程:
而通常在开发环境中服务端会使用自签名证书,或者即使在生产环境中可能也会使用免费的证书,这些证书的认证机构可信度通常低于那些收费的大型认证机构,这样,在步骤3的时候客户端对证书的认证可能就失败了。最近在开发公司的iOS SDK就碰到这个问题,直接调用HTTPS接口会报错:
HTTP load failed (kCFStreamErrorDomainSSL, -9843)
明白了原理后,解决方案就简单了,只需要客户端在校验时对使用的域名设置白名单就行了,以下是使用NSURLSession时需要在delegate上做的修改:
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:Nil]; - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{ if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){ if([challenge.protectionSpace.host isEqualToString:@"recvapi.log.dtstack.com"]){ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential,credential); } else { completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); } } }
这样就可以解决开发时的证书校验失败的问题。
但这只限于开发阶段,治标不治本,不能在生产环境下使用,要想治本,最好还是更换服务器证书,找个可信度高的大型认证机构重新弄份证书。
服务端更换证书也简单,以Nginx为例,拿到证书后,修改Nginx的配置:
server { listen 443 ssl; server_name example.com; ssl on; ssl_certificate /etc/ssl/private/example_com.crt; ssl_certificate_key /etc/ssl/private/example_com.key; }
然后重启服务就行了:
nginx -t && nginx -s reload