0x00 问题引出
调用NSURLConnection实现HTTPS访问时,如果服务器证书是由CA机构颁发的(全球可信的机构,如verisign),连接方式和HTTP并没有区别。代码如下:
NSString *urlStr = @"https://yourURL";
NSURL *url = [NSURL URLWithString:urlStr];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
系统会帮你建立TLS连接,验证服务器证书(包含是否过期,证书和访问域名是否一致,证书是否为合法机构颁发等),交换对称加密密钥等,几乎不用自己写任何代码。
0x01 ATS
但是iOS9有一个新特性ATS(App Transport Security),需要我们注意一下,在测试访问百度时发现报错:
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
错误代码定义详解见文件<Security/SecureTranspot.h>,所有相关的错误代码都定义在这个头文件里面。查阅后发现-9802描述为Fatal alert。常见的还有:-9843(域名和证书不符),-9814(证书链中有过期证书)等。
同样的代码在iOS9以前的系统上就不存在问题,这是因为在iOS9之后,ATS默认打开,然后它要求所有网络请求都为HTTPS,且支持TLS V1.2,对加密方式和证书的签名算法强度均有要求(详情见,iOS App Transport Security研究)。官方文档是这样描述的:
Certificates must be signed with one of the following types of keys:
- Secure Hash Algorithm 2 (SHA-2) key with a digest length of at least 256 (that is, SHA-256 or greater)
- Elliptic-Curve Cryptography (ECC) key with a size of at least 256 bits
- Rivest-Shamir-Adleman (RSA) key with a length of at least 2048 bits
An invalid certificate results in a hard failure and no connection.
也就是说不满足条件的证书,ATS都会拒绝连接,百度证书如下图。由于它采用的sha1,不满足要求,因此会报错。