iOS 中 AFNetworking HTTPS 的使用
由于我们公司由HTTP转HTTPS,出现了一系列问题特此记录下。
- 一、HTTPS
- 二、App Transport Security
- 三、iOS 中用HTTPS 注意的问题
- 四、使用 AFNetworking HTTPS 遇到的问题
- 五、问题的解决方法
- 六、注意服务器端 两种证书的区别(以及如何验证HTTPS服务器是否符合ATS特性中的要求)
一、HTTPS
HTTPS就是将HTTP协议数据包放到SSL/TSL层加密后,在TCP/IP层组成IP数据报去传输,以此保证传输数据的安全;而对于接收端,在SSL/TSL将接收的数据包解密之后,将数据传给HTTP协议层,就是普通的HTTP数据。HTTP和SSL/TSL都处于OSI模型的应用层。
二、App Transport Security
iOS9中新增App Transport Security(简称ATS)特性, 主要使到原来请求的时候用到的HTTP,都转向TLS1.2协议进行传输。这也意味着所有的HTTP协议都强制使用了HTTPS协议进行传输。
一般我们如果还是使用的http,不更新的话,可通过在 Info.plist 中声明,倒退回不安全的网络请求。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
三、iOS 中用HTTPS 注意的问题
先看文档中的描述:
These are the App Transport Security requirements:
- The protocol Transport Security Layer (TLS) must be at least version 1.2.
- Connection ciphers are limited to those that provide forward secrecy (see the list of ciphers below.)
- Certificates must use at least an SHA256 fingerprint with either a 2048 bit or greater RSA key, or a 256 bit or greater Elliptic-Curve (ECC) key.
根据原文描述,首先必须要基于TLS 1.2版本协议。再来就是连接的加密方式要提供Forward Secrecy,文档中罗列出支持的加密算法(如下表)。最后就是证书至少要使用一个SHA256的指纹与任一个2048位或者更高位的RSA密钥,或者是256位或者更高位的ECC密钥。如果不符合其中一项,请求将被中断并返回nil。
-
第一条就是TLS版本所需要支持的协议
第一条满足 -
第一条就是连接的加密方式需要提供“Foward Secrecy”这个东东,下面是支持Forward Secrecy的加密方式
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA `TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256` TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
这是一个满足条件的证书信息中的情况
满足上面中的条件
证书不被信任 证书未经过验证 有效但是也要注意证书的合法性,注意是否有效,iOS要求连接的HTTPS站点必须为CA签名过的合法证书。
注意以上不同的情况决定了AFSecurityPolicy--setAllowInvalidCertificates:
是否要验证证书的有效性。
四、使用AFNetworking HTTPS 遇到的问题(3.0.0之前)
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
//allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO//如果是需要验证自建证书,需要设置为YES
securityPolicy.allowInvalidCertificates = YES;
//validatesDomainName 是否需要验证域名,默认为YES;
securityPolicy.validatesDomainName = YES;
manager.securityPolicy = securityPolicy;
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager POST:urlString parameters:dic success:finishedBlock failure:failedBlock];
但是刚开始一直不行,不能正常访问数据
- 第一个错是我打开
Clarles
,忘记关了 - 老出现`Error: Error Domain=NSURLErrorDomain Code=-1012
第二种情况,在google逛了一大圈,试着好几种方法居然不行,💢💢💢💢,特别是老看到The operation couldn’t be completed. (NSURLErrorDomain error -1012.)
,头都大了。。。
五、直到后来找到了一种解决方法...
步骤1:获取到站点的证书
我们可以使用以下openssl命令来获取到服务器的公开二进制证书(以google为例)
openssl s_client -connect www.google.com:443 </dev/null 2>/dev/null | openssl x509 -outform DER > https.cer
冒号中的为命令主要部分。该条命令将会在当前路径下,形成google.com站点的公开二进制证书,命名为https.cer。您可以将www.google.com 替换成您自己的站点以此来获取您自己站点的https.cer。
步骤2:导入到Xcode中
直接将https.cer放到资源目录中就好了,让我们可以通过pathForResource:
获取到证书,就OK了。
步骤3:代码导入
NSString *urlString = @"https://www.example.com/app/publicRequest";
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"https" ofType:@"cer"];
NSData * certData =[NSData dataWithContentsOfFile:cerPath];
NSSet * certSet = [[NSSet alloc] initWithObjects:certData, nil];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
// 是否允许,NO-- 不允许无效的证书
[securityPolicy setAllowInvalidCertificates:YES];
// 设置证书
[securityPolicy setPinnedCertificates:certSet];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
// request
[manager GET:urlString parameters:nil progress:^(NSProgress * progress){
} success:^(NSURLSessionDataTask *task, id responseObject) {
NSArray * array = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"OK === %@",array);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"error ==%@",error.description);
}];
然后,就OK了。
六、注意下服务器端两种证书的区别
- 第一种是创建证书请求,然后到权威机构认证,随之配置到服务器;
- 第二种是自建证书,需要自己配置给服务器。
使用第一种还是好一点的,至少在我们 app 端不需要为ATS做过多的适配。但是如何才能知道一个HTTPS服务器是否符合ATS特性中的要求的呢?使用nscurl命令:
nscurl --ats-diagnostics --verbose https://example.com
下面是测百度时,返回的某一段,结论是OK的,里面有很多测试的情况,我们可以逐一观察是否正确。
TLSv1.0 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"www.baidu.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.0";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
PS:现在我新公司的项目这边是都不要做之前那种调整适配的,使用是第一种 权威 CA 颁发的那种证书,我们 APP 这端实际上真正的工作量是没有多少的,所以说关于HTTPS 适配的这块:
可以说:在购买到合法的证书时,我们 App 端 基本什么都不用管,改一下URL就好啦。
参考
http://my.oschina.net/vimfung/blog/494687
http://www.jianshu.com/p/4102b817ff2f
http://oncenote.com/2014/10/21/Security-1-HTTPS/
http://nelson.logdown.com/posts/2015/04/29/how-to-properly-setup-afnetworking-security-connection/
https://zhuanlan.zhihu.com/p/22749689