http://oncenote.com/2014/10/21/Security-1-HTTPS/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

1. HTTPS

其实HTTPS从最终的数据解析的角度,与HTTP没有任何的区别,HTTPS就是将HTTP协议数据包放到SSL/TSL层加密后,在TCP/IP层组成IP数据报去传输,以此保证传输数据的安全;而对于接收端,在SSL/TSL将接收的数据包解密之后,将数据传给HTTP协议层,就是普通的HTTP数据。HTTP和SSL/TSL都处于OSI模型的应用层。从HTTP切换到HTTPS是一个非常简单的过程,在做具体的切换操作之前,我们需要了解几个概念:

SSL/TSL

关于SSL/TSL,阮一峰的两篇博客文章做了很好的介绍:

简单的来说,SSL/TSL通过四次握手,主要交换三个信息:

  1. 数字证书:该证书包含了公钥等信息,一般是由服务器发给客户端,接收方通过验证这个证书是不是由信赖的CA签发,或者与本地的证书相对比,来判断证书是否可信;假如需要双向验证,则服务器和客户端都需要发送数字证书给对方验证;
  2. 三个随机数:这三个随机数构成了后续通信过程中用来对数据进行对称加密解密的“对话密钥”。

    首先客户端先发第一个随机数N1,然后服务器回了第二个随机数N2(这个过程同时把之前提到的证书发给客户端),这两个随机数都是明文的;而第三个随机数N3(这个随机数被称为Premaster secret),客户端用数字证书的公钥进行非对称加密,发给服务器;而服务器用只有自己知道的私钥来解密,获取第三个随机数。这样,服务端和客户端都有了三个随机数N1+N2+N3,然后两端就使用这三个随机数来生成“对话密钥”,在此之后的通信都是使用这个“对话密钥”来进行对称加密解密。因为这个过程中,服务端的私钥只用来解密第三个随机数,从来没有在网络中传输过,这样的话,只要私钥没有被泄露,那么数据就是安全的。
  3. 加密通信协议:就是双方商量使用哪一种加密方式,假如两者支持的加密方式不匹配,则无法进行通信;

有个常见的问题,关于随机数为什么要三个?只最后一个随机数N3不可以么?

这是由于SSL/TLS设计,就假设服务器不相信所有的客户端都能够提供完全随机数,假如某个客户端提供的随机数不随机的话,就大大增加了“对话密钥”被破解的风险,所以由三组随机数组成最后的随机数,保证了随机数的随机性,以此来保证每次生成的“对话密钥”安全性。

数字证书

数字证书是一个电子文档,其中包含了持有者的信息、公钥以及证明该证书有效的数字签名。而数字证书以及相关的公钥管理和验证等技术组成了PKI(公钥基础设施)规范体系。一般来说,数字证书是由数字证书认证机构(Certificate authority,即CA)来负责签发和管理,并承担PKI体系中公钥合法性的检验责任;数字证书的类型有很多,而HTTPS使用的是SSL证书。

怎么来验证数字证书是由CA签发的,而不是第三方伪造的呢? 在回答这个问题前,我们需要先了解CA的组织结构。首先,CA组织结构中,最顶层的就是根CA,根CA下可以授权给多个二级CA,而二级CA又可以授权多个三级CA,所以CA的组织结构是一个树结构。对于SSL证书市场来说,主要被Symantec(旗下有VeriSign和GeoTrust)、Comodo SSL、Go Daddy 和 GlobalSign 瓜分。 了解了CA的组织结构后,来看看数字证书的签发流程:

数字证书的签发机构CA,在接收到申请者的资料后进行核对并确定信息的真实有效,然后就会制作一份符合X.509标准的文件。证书中的证书内容包含的持有者信息和公钥等都是由申请者提供的,而数字签名则是CA机构对证书内容进行hash加密后得到的,而这个数字签名就是我们验证证书是否是有可信CA签发的数据。

假设上图证书是由证书签发机构CA1签发的。

1)接收端接到一份数字证书Cer1后,对证书的内容做Hash得到H1;

2)从签发该证书的机构CA1的数字证书中找到公钥,对证书上数字签名进行解密,得到证书Cer1签名的Hash摘要H2;

3)对比H1和H2,如相等,则表示证书没有被篡改。

4)但这个时候还是不知道CA是否是合法的,我们看到上图中有CA机构的数字证书,这个证书是公开的,所有人都可以获取到。而这个证书中的数字签名是上一级生成的,所以可以这样一直递归验证下去,直到根CA。根CA是自验证的,即他的数字签名是由自己的私钥来生成的。合法的根CA会被浏览器和操作系统加入到权威信任CA列表中,这样就完成了最终的验证。所以,一定要保护好自己环境(浏览器/操作系统)中根CA信任列表,信任了根CA就表示信任所有根CA下所有子级CA所签发的证书,不要随便添加根CA证书。

一般操作系统和浏览器只包含根CA机构的证书,而在配置Web服务器的HTTPS时,也会将配置整个证书链,所以整个校验流程是从最后的叶子节点证书开始,用父节点校验子节点,一层层校验整个证书链的可信性。

打个比喻:父(根CA数字证书)-子(CA数字证书)-孙(数字证书)三代人,假设父没有其他兄弟(相当于根CA机构是唯一的),假如子与父进行DNA亲子鉴定,检测DNA位点(即证书签名)相同,那就基本确定子是由父所生;孙与子一样。这样就能够确定孙肯定是源于父一脉,是父(根CA数字证书)的合法继承人。数字证书的验证就是基于同样的原理。

2. 实现支持HTTPS

首先,需要明确你使用HTTP/HTTPS的用途,因为OSX和iOS平台提供了多种API,来支持不同的用途,官方文档《Making HTTP and HTTPS Requests》有详细的说明,而文档《HTTPS Server Trust Evaluation》则详细讲解了HTTPS验证相关知识,这里就不多说了。本文主要讲解我们最常用的NSURLConnection支持HTTPS的实现(NSURLSession的实现方法类似,只是要求授权证明的回调不一样而已),以及怎么样使用AFNetworking这个非常流行的第三方库来支持HTTPS。本文假设你对HTTP以及NSURLConnection的接口有了足够的了解。

验证证书的API

相关的Api在Security Framework中,验证流程如下:

1). 第一步,先获取需要验证的信任对象(Trust Object)。这个Trust Object在不同的应用场景下获取的方式都不一样,对于NSURLConnection来说,是从delegate方法-connection:willSendRequestForAuthenticationChallenge:回调回来的参数challenge中获取([challenge.protectionSpace serverTrust])。

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

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

4). 假如有更强的安全要求,可以继续对Trust Object进行更严格的验证。常用的方式是在本地导入证书,验证Trust Object与导入的证书是否匹配。更多的方法可以查看Enforcing Stricter Server Trust Evaluation,这一部分在讲解AFNetworking源码中会讲解到。

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

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

iOS授权验证的API和流程大概了解了,下面,我们看看在NSURLConnection中的代码实现:

使用NSURLConnection支持HTTPS的实现

// Now start the connection

NSURL * httpsURL = [NSURL URLWithString:@"https://www.google.com"];

self.connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:httpsURL] delegate:self];

//回调

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

//1)获取trust object

SecTrustRef trust = challenge.protectionSpace.serverTrust;

SecTrustResultType result;

//2)SecTrustEvaluate对trust进行验证

OSStatus status = SecTrustEvaluate(trust, &result);

if (status == errSecSuccess &&

(result == kSecTrustResultProceed ||

result == kSecTrustResultUnspecified)) {

//3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接

NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];

[challenge.sender useCredential:cred forAuthenticationChallenge:challenge];

} else {

//5)验证失败,取消这次验证流程

[challenge.sender cancelAuthenticationChallenge:challenge];

}

}

上面是代码是通过系统默认验证流程来验证证书的。假如我们是自建证书的呢?这样Trust Object里面服务器的证书因为不是可信任的CA签发的,所以直接使用SecTrustEvaluate进行验证是不会成功。又或者,即使服务器返回的证书是信任CA签发的,又如何确定这证书就是我们想要的特定证书?这就需要先在本地导入证书,设置成需要参与验证的Anchor Certificate(锚点证书,通过SecTrustSetAnchorCertificates设置了参与校验锚点证书之后,假如验证的数字证书是这个锚点证书的子节点,即验证的数字证书是由锚点证书对应CA或子CA签发的,或是该证书本身,则信任该证书),再调用SecTrustEvaluate来验证。代码如下

//先导入证书

NSString * cerPath = ...; //证书的路径

NSData * cerData = [NSData dataWithContentsOfFile:cerPath];

SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));

self.trustedCertificates = @[CFBridgingRelease(certificate)];

//回调

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

//1)获取trust object

SecTrustRef trust = challenge.protectionSpace.serverTrust;

SecTrustResultType result;

//注意:这里将之前导入的证书设置成下面验证的Trust Object的anchor certificate

SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)self.trustedCertificates);

//2)SecTrustEvaluate会查找前面SecTrustSetAnchorCertificates设置的证书或者系统默认提供的证书,对trust进行验证

OSStatus status = SecTrustEvaluate(trust, &result);

if (status == errSecSuccess &&

(result == kSecTrustResultProceed ||

result == kSecTrustResultUnspecified)) {

//3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接

NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];

[challenge.sender useCredential:cred forAuthenticationChallenge:challenge];

} else {

//5)验证失败,取消这次验证流程

[challenge.sender cancelAuthenticationChallenge:challenge];

}

}

建议采用本地导入证书的方式验证证书,来保证足够的安全性。更多的验证方法,请查看官方文档《HTTPS Server Trust Evaluation》

使用AFNetworking来支持HTTPS

AFNetworking是iOS/OSX开发最流行的第三方开源库之一,其作者是非常著名的iOS/OSX开发者Mattt Thompson,其博客NSHipster也是iOS/OSX开发者学习和开阔技术视野的好地方。AFNetworking已经将上面的逻辑代码封装好,甚至更完善,在AFSecurityPolicy文件中,有兴趣可以阅读这个模块的代码;

AFNetworking上配置对HTTPS的支持非常简单:

NSURL * url = [NSURL URLWithString:@"https://www.google.com"];

AFHTTPRequestOperationManager * requestOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:url];

dispatch_queue_t requestQueue = dispatch_create_serial_queue_for_name("kRequestCompletionQueue");

requestOperationManager.completionQueue = requestQueue;

AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

//allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO

//如果是需要验证自建证书,需要设置为YES

securityPolicy.allowInvalidCertificates = YES;

//validatesDomainName 是否需要验证域名,默认为YES;

//假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。

//置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。

//如置为NO,建议自己添加对应域名的校验逻辑。

securityPolicy.validatesDomainName = YES;

//validatesCertificateChain 是否验证整个证书链,默认为YES

//设置为YES,会将服务器返回的Trust Object上的证书链与本地导入的证书进行对比,这就意味着,假如你的证书链是这样的:

//GeoTrust Global CA

//    Google Internet Authority G2

//        *.google.com

//那么,除了导入*.google.com之外,还需要导入证书链上所有的CA证书(GeoTrust Global CA, Google Internet Authority G2);

//如是自建证书的时候,可以设置为YES,增强安全性;假如是信任的CA所签发的证书,则建议关闭该验证,因为整个证书链一一比对是完全没有必要(请查看源代码);

securityPolicy.validatesCertificateChain = NO;

requestOperationManager.securityPolicy = securityPolicy;

这就是AFNetworking的支持HTTPS的主要配置说明,AFHTTPSessionManager与之基本一致,就不重复了。

使用ASIHttpRequest来支持HTTPS

一种方法

ASIHTTPRequest *request = [ASIHTTPRequestrequestWithURL:[NSURLURLWithString:bodyString]];

[request setDelegate:self];

request.timeOutSeconds = 20;

[request setRequestMethod:@"POST"];

[request addRequestHeader:@"Content-Type"value:@"application/xml;charset=UTF-8;"];

[request setValidatesSecureCertificate:NO];//请求https的时候,就要设置这个属性

[request startAsynchronous];

二种方法

ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:bodyString]];

[request setDelegate:self];

request.timeOutSeconds = 20;

[request setValidatesSecureCertificate:NO];//请求https的时候,就要设置这个属性

[request setRequestMethod:@"POST"]; [request addRequestHeader:@"Content-Type" value:@"application/xml;charset=UTF-8"]; request.defaultResponseEncoding = NSUTF8StringEncoding;

使用ASIHttpRequest来支持HTTPS

一种方法
ASIHTTPRequest *request = [ASIHTTPRequestrequestWithURL:[NSURLURLWithString:bodyString]];

[request setDelegate:self];

request.timeOutSeconds = 20;

[request setRequestMethod:@"POST"];

[request addRequestHeader:@"Content-Type"value:@"application/xml;charset=UTF-8;"];

[request setValidatesSecureCertificate:NO];//请求https的时候,就要设置这个属性

[request startAsynchronous];

二种方法
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:bodyString]];
[request setDelegate:self];
request.timeOutSeconds = 20;
[request setValidatesSecureCertificate:NO];//请求https的时候,就要设置这个属性
[request setRequestMethod:@"POST"]; [request addRequestHeader:@"Content-Type" value:@"application/xml;charset=UTF-8"]; request.defaultResponseEncoding = NSUTF8StringEncoding;

iOS支持Https的更多相关文章

  1. IOS 支持HTTPS调用(AFNetWorking框架)

    1.ATS开关开启2.manager.securityPolicy.allowInvalidCertificates = YES; manager.securityPolicy.validatesDo ...

  2. 在iOS APP中使用H5显示百度地图时如何支持HTTPS?

    现象: 公司正在开发一个iOSAPP,使用h5显示百度地图,但是发现同样的H5页面,在安卓可以显示出来,在iOS中就显示不出来. 原因分析: 但是现在iOS开发中,苹果已经要求在APP中的所有对外连接 ...

  3. iOS app 支持HTTPS iOS开发者相关

    2016年12月21日更新开发者中心链接https://developer.apple.com/news/?id=12212016b该链接是苹果昨天刚在官网给的正式回复 如下: App Transpo ...

  4. iOS开发 支持https请求以及ssl证书配置(转)

    原文地址:http://blog.5ibc.net/p/100221.html 众所周知,苹果有言,从2017年开始,将屏蔽http的资源,强推https 楼主正好近日将http转为https,给还没 ...

  5. iOS 10 适配 ATS(app支持https通过App Store审核)

    iOS 10 适配 ATS 一. HTTPS 其实HTTPS从最终的数据解析的角度,与HTTP没有任何的区别,HTTPS就是将HTTP协议数据包放到SSL/TSL层加密后,在TCP/IP层组成IP数据 ...

  6. iOS 升级HTTPS通过ATS你所要知道的

    由于苹果规定2017年1月1日以后,所有APP都要使用HTTPS进行网络请求,否则无法上架,因此研究了一下在iOS中使用HTTPS请求的实现.网上搜索了一些比较有用资料,大家可以参考下 苹果强制升级的 ...

  7. iOS 适配https

    1.准备证书 首先找后台要一个证书(SSL证书,一般你跟后台说要弄https,然后让他给你个证书,他就知道了),我们需要的是.cer的证书.但是后台可能给我们的是.crt的证书. 我们需要转换一下: ...

  8. iOS开发HTTPS实现之信任SSL证书和自签名证书

    iOS开发HTTPS实现之信任SSL证书和自签名证书 转自:http://www.jianshu.com/p/6b9c8bd5005a/comments/5539345 (收录一下供自己学习用的) 字 ...

  9. 支持https请求以及https请求的抓包

    iOS9推出的时候,苹果希望大家使用https协议,来提高数据传输之间的安全性.下面我就从最简单的代码介绍,如何在工程中设置,来支持https的请求. 一.证书准备篇 1.证书转换 在服务器人员,给你 ...

随机推荐

  1. memcpy和memmove

    memcpy函数 函数原型 void *memcpy(void *dest, const void *src, size_t n); dest:目标地址 src: 起始地址 n: 字节数 头文件 st ...

  2. 网络编程(一):用C#下载网络文件的2种方法

    使用C#下载一个Internet上的文件主要是依靠HttpWebRequest/HttpWebResonse和WebClient.具体处理起来还有同步和异步两种方式,所以我们其实有四种组合. 1.使用 ...

  3. C/C++二维数组分配内存

    //C++方式 double **Q=new double*[row];    //初始化Q矩阵 for(int i=0;i<row;++i) Q[i]=new double[POS_NUM]( ...

  4. 牛客网程序员面试金典:1.2——原串翻转(java实现)

    问题描述: 请实现一个算法,在不使用额外数据结构和储存空间的情况下,翻转一个给定的字符串(可以使用单个过程变量). 给定一个string iniString,请返回一个string,为翻转后的字符串. ...

  5. UML6大关系

    1.继承关系(inherit) 空心三角+实线 2.实现(接口)关系 空心三角+虚线 3.聚合关系(Aggregation,弱拥有,A对象可以包含B对象,但B对象不是A的一部分) 空心菱形+实线箭头 ...

  6. android layout布局属性

    参考:http://blog.csdn.net/msmile_my/article/details/9018775 第一类:属性值 true或者 false           android:lay ...

  7. SVN Cornerstone 报错信息 xcodeproj cannot be opened because the project file cannot be parsed.

    svn点击update 之后,打开xcode工程文件,会出现  xxx..xcodeproj  cannot be opened becausethe project file cannot be p ...

  8. Ubuntu Kylin 14.04-修改IP固定地址

           前言:今天我们办公室的网络不稳定,隔一会时间就断掉,后来请负责网络的同事来处理了一下,说是路由器的有些配置起冲突了,所以他将IP地址做了一些修改,比如:原IP是192.168.0.110 ...

  9. socket 网络编程

    1. 基础socket库 socket.h: /** * 网络套接字库 */ #ifndef Socket_h #define Socket_h #include <stdio.h> #i ...

  10. 二模13day1解题报告

    二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...