iOS 9.0之后苹果开始要求使用Https进行通信。ATS是iOS9和OS X El Capitan的一个新特性。开启该功能后,ATS对使用NSURLConnection, CFURL或NSURLSession 等APIs 进行的网络请求默认强制使用HTTPS加密传输,目标是提高Apple 操作系统以及应用程序的安全性。苹果公司官方文章指出,https必须符合ATS要求,服务器必须支持传输层安全(TLS)协议1.2以上版本;证书必须使用SHA256或更高的哈希算法签名,并使用2048位以上RSA密钥或256位以上ECC算法等等。
https概要
- https协议基于http(超文本传输协议),在http通信基础上对传输报文进行加密,主要是为了保证通信双方的数据不被窃取。在通信时,服务器和客户端各自提供自己的凭证,验证自己的身份后进行通信. 这样能减少不受信任的三方窃取信息。
- PKI(公钥基础设施),是HTTPS的基础,PKI与非堆成秘钥加密技术密切相关,包括消息摘要,数字签名,和加密服务。而数字证书以及证书机构(CA -Certificate Authority)是PKI中重要的概念。
- 数字证书在https中也起着至关重要的作用, 它是一个计算机文件,一般由可信任的证书机构颁发,他包含了证书所有者的一般信息(公开秘钥)。并且能够证明这个公钥确实是证书所有者所合法拥有的。这一点是有CA对数字证书的签名来保证的(签名用到了信息摘要以及非对称加密算法,使用CA的私钥加密),在这里我门需要使用CA的数字证书(包含CA的公钥)来验证签名的合法性,那么我门如何验证CA数字证书的合法性呢?CA的信任链可以解决这个问题。一个CA的证书是由上一级CA签发的,因此它的合法性由它的上一级CA来验证,其中最顶层的证书机构被称为根CA,它的证书被称为根证书,如果一个链条的根证书是合法可信的,那么我们就认为在这个链条上的所有CA以及它们所签发的证书都是合法可信的。于是,最终的问题就是如何保证根证书的合法性呢?原来,我们所使用的基本计算机软件,比如浏览器和服务器软件,都会内置根CA的自签名证书,只要我们使用的基本软件可信,那么就能保证根CA证书的合法有效。
https图解
- https双向通信,只是在服务器身份确认后,提供客户端凭证再进行一次客户端的身份校验过程。首先客户端和服务器需要经过三次确认,生成二个随机数,并将服务器的证书提供給客户端。前面两个随机数因为时明文传输的,很容易給截取到,当客户端验证完服务器端的证书OK后,通过随机数扩展算法,按一定的规则生成第三个随机数,并用服务器的公钥加密后发送給服务器.这样客户端和服务器均获得三个随机数,从而可以生成双方约定的会话秘钥。 客户端再将自己的证书发送給服务器端进行验证,只要服务器验证通过,那么他们就开始使用这个随机秘钥进行数据加密。实际上如果, 客户端和服务器端协议版本号不一致,客户端偏好设置中的加密算法列表里没有服务器支持的任何一种加密算法,客户端的凭证或服务器的凭证无法匹配,服务器域名不正确,或者证书过期等,都会导致通信失败,这时候根据指定的警报协议 将相关的失败信息通知到接收方。
-
下图为https的单向通信的主要流程,实际要比这个要复杂的多(忽略上面客户端凭证的校验过程)。
https通信详解
- HTTPS是工作于SSL层上的HTTP协议,SSL(安全套接层)工作于TCP层上,向应用层提供了两个基本的安全服务,认证和保密。 SSL有三个子协议, 握手协议,记录协议,警报协议。
- 握手协议:
- 建立安全能力: 由客户端发起,向服务器发送Client Hello消息,其中包含SSL版本,客户端随机数(用于生成秘钥),会话号,加密算法清单(客户端所支持的加密算法),压缩算法清单。服务器返回 Server Hello信息,其中包含SSL版本,服务器随机数(用于生成秘钥),会话号,选择加密算法,选择的压缩算法。
- 服务器认证与秘钥交换服务器是本阶段发送信息的所有方,共三步
- 第一步 : 证书,服务器将数字证书以及CA证书链发给客户端,客户端由此获得服务器公钥;客户端还可以再这一步验证服务器是否可信.如果不可信则可以停止链接。并提醒用户注意。
- 第二步 : 服务器请求客户端证书,客户端认证在SSL中是可选的,因此这一步也是可选的。
- 第三步 : 服务器握手完成,发送这个消息后,服务器等待客户端的响应。
- 客户端认证与秘钥交换
- 客户端是本阶段的所有信息的发送方,分为三步
- 第一步 : 证书,客户端将客户端的数字证书发送给服务器,里面包含了客户端的公钥,通常来说这个证书应该是由服务提供着分发给客户端,由指定的CA签发的,因此服务器可以验证客户端证书的合法性,并决定是否继续。
- 第二步 : 秘钥交换,客户端生成48字节的预备秘钥,并用服务器端的公钥加密,然后发送给服务器,这个预备秘钥只有客户端和服务器知道(与双发在之后会话中使用的对成秘钥相关),在这一步也间接的验证了服务器的合法性.因为只有拥有与证书对应的私钥才能解密出预备秘钥。
- 第三步 : 证书验证,客户端还需要向服务器验证自己是真正的客户端(数字证书无法证明它就是客户端,拥有阈公钥对应的私钥才是关键),为此客户端把预备秘钥,客户随机数,服务器的随记数组和起来,用私钥对结果进行签名,发送给服务器,服务器利用客户端公钥就能够的到原始的数据,用来验证客户端的真实性。(这里主要是对 客户端随记数,服务器随记数,预备秘钥)这个三个要素进行判定是否有人窃取和串改。
- 完成: 在这个阶段,客户端和服务器各自独立生成相同的主秘钥和对成秘钥,主秘钥和对成秘钥只有它们自己知道。主秘钥和对成秘钥是由 预备秘钥,客户端随机数和服务器随机数组和后,经过消息摘要算法生成。
- 握手协议:
- SSL握手完成之后,就会进入回话阶段:客户端和服务器使用握手协议中生成的对成秘钥进行加密和解密以保证通信的安全。
- 总的来说, 如果钥完成HTTPS的双向认证需要以下秘钥和证书:
- 服务器端: 1.服务器私钥, 2.由CA前发的含有服务器公钥的数字证书, 3.CA的数字证书,在双向验证过程中通常服务器可以自己作为证书机构,并且由服务器CA前发服务器证书和客户端证书。
- 客服端: 1.客户端私钥, 2. 由CA签发的含有客户端公钥的数字证书。为了避免中间人攻击,客户端还需要内置服务器证书,用来验证所连接的服务器是否是指定的服务器。(通常在一些服务器的网站或者app中,客服端凭证校验是默认省略的,单向安全通信的,只对服务器的身份进行校验)。
https实践
- 在Apple Reference《Certificate,Key,and Trust Services Programming Guide》提供了https验证的框架, security.该框架包含了 https通信中信任对象, 同时也提供https加密中SAH,MD5,RSA,AES等算法的支持。
- 基于AFNetworking https单向验证,系统框架已经做了非常多的简化步骤,在AFSecurity这个库中,框架内部已经做好了相关的判定,比如根据是否为私有证书,将提供的 证书对象设置为锚证书,对服务器返回的 信任对象进行评估,如果通过则进行安全通信,反之则取消通信。
+ (AFSecurityPolicy*)configSecurityPolicy {
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"https" ofType:@"cer"];//证书的路径
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
AFSecurityPolicy* securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.allowInvalidCertificates = YES; /**如果采用三方颁发的证书,则不使用自建证书验证服务器,由三方机构验证*/
securityPolicy.validatesDomainName = YES;
securityPolicy.pinnedCertificates = [NSSet setWithObject:certData];
mgr.securityPolicy = securityPolicy;
return securityPolicy;
}
- 基于AFNetworking双向通信。
- 在AFNetworking中进行双向通信,需要我们自己对挑战认证进行定义,其中服务器身份验证部分,我们仍然可以沿用上面的 security对象进行自动校验,但是客户端验证,就需要我们进行实现了。
- AFNetworking框架已经为我们讲调整验证的方法是用回调Block抽离出来。在使用时候我们只需钥注册该回调block,在对应的block中实现当收到验证服务器端凭证和需要提供客服端凭证的方法进行处理.相关代码实现方式如下.
- 服务端验证: 首先需要对挑战challege对象的保护空间进行判定,获取当前的验证方式,如果是对服务器验证,则取出serverTrust对象,并通过设定锚证书,与我们定义的服务器证书的信任对象进行比对和评估,判定是否正确,如果正确,则告诉challege挑战发起者,下次继续使用该凭证作为服务器的身份验证。
- 客户端验证: 将客服端凭证对象,通常都是由p12文件生成, 先将它转换为CFData类型,再通过 valueForKey的方式,一层一层的将其拨开,取到最后的秘钥,并生成NSURLCredential凭证对象。
- AFNetworking框架最终生成的凭证对象,以及验证方式,通过block的回调的方式在 delegate挑战认证的方法中回调出去。
+ (void)configSSLChallage {
AFHTTPSessionManager* mgr = [self shareInstance];
__weak typeof(*&mgr)weakmgr = mgr;
[mgr setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing _Nullable * _Nullable credential) {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__autoreleasing NSURLCredential *_credential =nil;
//server 端验证:
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSLog(@"authorMethod:%@",challenge.protectionSpace.authenticationMethod);
if([weakmgr.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host] ) {
_credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if(credential) {
disposition =NSURLSessionAuthChallengeUseCredential;
} else {
disposition =NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
//client 客户端验证:
NSLog(@"authorMethod:%@",challenge.protectionSpace.authenticationMethod);
// client authentication
SecIdentityRef identity = NULL;
SecTrustRef trust = NULL;
NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];
NSFileManager *fileManager =[NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:p12])
{
NSLog(@"client.p12:not exist");
}
else
{
NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
if ([PLMNetWorkTool extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
{
if (extractIdentityAndTrust((__bridge CFDataRef)(PKCS12Data), &identity, &trust)== noErr) {
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate(identity, &certificate);
const void*certs[] = {certificate};
CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
_credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
disposition =NSURLSessionAuthChallengeUseCredential;
}
}
}
}
*credential = _credential;
return disposition;
}];
}
总结: https涉及的内容较多,如果不明白其中的具体原理,仅仅靠搬运一份代码,那么一段出现问题将是非常致命的。理解其中的缘由对于我们应对https证书伪造,域名劫持,或是https会话秘钥扩展,https安全通信双层加固非常重要。
参考文献: http://baike.baidu.com/link?url=QDGRYuHehLYwvUSNX_vH7xo8tQ8CJoDSSKeLP_PWzmohvk5E1E6RegmRGB0hERJW_0MqTBJn6sp6h65hLi_04a
http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html