AFSecurityPolicy是AFNetworking中负责对https请求进行证书验证的模块,本文主要是要搞清楚它是如何工作的。

在介绍AFSecurityPolicy之前,我们先来了解一下https以及一些相关概念。

HTTPS

简单来说,https是运行在SSL/TLS之上的http,是为了提升数据传输的安全性的,使用到了对称加密和非对称加密算法。让我们通过客户端与服务端的四次交互(四次握手)来详细看看https都做了些什么。

重点说下第2步和第3步。

第2步主要是要将服务端的公钥安全的发送给客户端,为此服务端主要做了两件事:

1、用服务端的私钥加密摘要

2、传递CA颁发的用CA的私钥加密的包含服务端公钥的证书

然后到了客户端(也就是第3步),客户端分几步来验证:

1、客户端先使用本地CA来验证证书是否授信,如果是权威机构颁发的证书,客户端会认为该证书是授信的(如果是自己搭建的CA,则会被认为是不授信的,会产生警告),同时也会验证证书的有效期,访问的域名是否一致等信息。

2、客户端根据证书的要求生成证书编号

3、然后客户端会用本地CA公钥解密证书,拿到证书编号,如果生成的证书编号和拿到的证书编号一致,则认为证书没有问题,从而拿到服务端的公钥(简称为SK)

4、然后使用SK来解密服务端私钥加密的摘要(简称SS),并且本地用加密算法将内容加密成摘要(简称LS),对比SS == LS

需要说明一下CA的私钥加密的包含服务端公钥的证书,这个证书是用来保证信息不被中间人掉包的。因为证书编号是由CA的私钥加密的,即使是中间人也无法拿到CA的私钥,而客户端的本地CA公钥只能解密由CA的私钥加密的证书编号,所以中间人无法伪造证书。

那么假设中间人自己也申请一个CA的证书,然后客户端请求的时候本来要请求服务端的证书A,中间人拦截以后,发回自己的证书B给客户端,这个时候对于证书编号的验证就不管用了,但是,证书A和证书B的域名是不同的,所以客户端在做验证的时候,就会认为证书不授信。

以上这些绕来绕去的流程就是https请求保证数据安全和防篡改的简易流程。也可以参考:

https://www.cnblogs.com/zhangshitong/p/6478721.html

我们在了解https的时候,会接触到一些相关常见的概念,如:SSL/TLS,openssl,PKI,CA,X.509等,下面我们来简单了解一下。

HTTPS相关概念

SSL:(Secure Socket Layer,安全套接字层),用以保障在Internet上数据传输安全。

TLS:(Transport Layer Security,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性。简单来看,可以认为TLS是SSL的升级版,比SSL更加安全。iOS9以后,已经要求TLS版本不低于1.2。

关于SSL和TLS的详情,可以参看:

http://www.cocoachina.com/ios/20150918/13488.html

http://seanlook.com/2015/01/07/tls-ssl/

PKI:(Public Key Infrastructure,公开密钥基础设施),是一个标准,用以为所有网络应用提供加密和数字签名等密码服务及所必须的秘钥和证书管理体系。CA是PKI的核心。

CA:(Certificate Authority,证书认证中心),是一个负责发放和管理数字证书的第三方权威机构,它负责管理PKI结构下的所有用户(包括各种应用程序)的证书,把用户的公钥和用户的其他信息捆绑在一起,在网上验证用户的身份。CA机构的数字签名使得攻击者不能伪造和篡改证书。前文所说的服务端证书,就是由CA颁发的。

关于PKI和CA的详情,可以参看:

http://netsecurity.51cto.com/art/200602/21066.htm

X.509:是PKI体系中最重要的标准。是一些标准字段的集合,这些字段包含有关用户或设备及其相应公钥的信息。包含:版本号,公钥,算法,序列号,主题信息,有效期,认证机构,数字签名等。可以参考:https://baike.baidu.com/item/x509/1240109?fr=aladdin

openssl:一个强大的安全套接字层密码库,大概可以分成三个主要的功能部分。

1、libcryto,这是一个具有通用功能的加密库,里面实现了众多的加密库。

2、libssl,这个是实现ssl机制的,它是用于实现TLS/SSL的功能。

3、openssl,是个多功能命令行工具,它可以实现加密解密,甚至还可以当CA来用,可以让你创建证书、吊销证书。

openssl生成私钥,公钥,并加密解密的简单实用如下:

  1. openssl genrsa -out rsakey0.pem //生成1024位rsa私钥
  2. openssl rsa -in rsakey0.pem -pubout -out rsaKeyPublic0 //生成公钥
  3. openssl rsautl -encrypt -in .txt -inkey rsaKeyPublic0.pem -pubin -out .txt //公钥加密文件
  4. openssl rsautl -decrypt -in .txt -inkey rsaKey0.pem -out .txt //私钥解密文件

在了解了https的过程及相关的概念作为铺垫以后,下面我们来看看AFSecurityPolicy到底做了什么。

AFSecurityPolicy

苹果已经为我们封装好了https连接的建立,加密解密的过程,但是并没有为我们验证证书是否合法,这一步需要在AFSecurityPolicy里面完成。

当实用AFNetworking来发起https的请求时,会调用委托:

  1. - (void)URLSession:(NSURLSession *)session
  2. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  3. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler{
  4. }

这是一个质询,需要确认认证信息才能完成连接。

  1. //判断服务器返回的证书类型,是否信任
  2. if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
  3. if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
  4. credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
  5. if (credential) {
  6. disposition = NSURLSessionAuthChallengeUseCredential;//使用指定的证书
  7. } else {
  8. disposition = NSURLSessionAuthChallengePerformDefaultHandling;//默认处理方式(忽略证书)
  9. }
  10. } else {
  11. disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;//取消质询
  12. }
  13. } else {
  14. disposition = NSURLSessionAuthChallengePerformDefaultHandling;
  15. }

重点看下:

  1. //判断服务端来的证书是否验证通过
  2. - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
  3. forDomain:(NSString *)domain
  4. {
  5. if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == )) {
  6. NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
  7. return NO;
  8. }
  9.  
  10. NSMutableArray *policies = [NSMutableArray array];
  11. //是否验证域名,如果你的请求要直接用ip去连,可以忽略域名验证,但是有风险,我们之前说过;下面无论if还是else都是创建不同的验证策略。
  12. if (self.validatesDomainName) {
  13. [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
  14. } else {
  15. [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
  16. }
  17.  
  18. //设置验证策略
  19. SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
  20.  
  21. if (self.SSLPinningMode == AFSSLPinningModeNone) {
  22. //客户端是否信任无效或过期的证书(可能是自签名证书)或者校验服务器传递的安全信息 serverTrust 是否是有效。
  23. return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
  24. } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
  25. return NO;
  26. }
  27.  
  28. switch (self.SSLPinningMode) {
  29. case AFSSLPinningModeNone:
  30. default:
  31. return NO;
  32. case AFSSLPinningModeCertificate: {
  33. //验证证书是否和本地的证书相同
  34. NSMutableArray *pinnedCertificates = [NSMutableArray array];
  35. for (NSData *certificateData in self.pinnedCertificates) {
  36. [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
  37. }
  38. //将本地证书的数据设置为校验服务器安全信息 serverTrust 的锚证书
  39. SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
  40.  
  41. if (!AFServerTrustIsValid(serverTrust)) {
  42. return NO;
  43. }
  44.  
  45. //获取所有服务端的证书,后面用以比较是否包含
  46. NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
  47.  
  48. for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
  49. if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
  50. return YES;
  51. }
  52. }
  53.  
  54. return NO;
  55. }
  56. case AFSSLPinningModePublicKey: {
  57. //验证本地证书的公钥和服务端的公钥是否相同
  58. NSUInteger trustedPublicKeyCount = ;
  59. //获取服务端的证书公钥
  60. NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
  61.  
  62. for (id trustChainPublicKey in publicKeys) {
  63. for (id pinnedPublicKey in self.pinnedPublicKeys) {
  64. if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
  65. trustedPublicKeyCount += ;
  66. }
  67. }
  68. }
  69. return trustedPublicKeyCount > ;
  70. }
  71. }
  72.  
  73. return NO;
  74. }

注释基本都加了,这里要说一下:SSLPinningMode(校验策略),分三种

AFSSLPinningModeNone: 这个模式表示不做SSL pinning,只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书。若证书是信任机构签发的就会通过,若是自己服务器生成的证书,这里是不会通过的。

AFSSLPinningModeCertificate:这个模式表示用证书绑定方式验证证书,需要客户端保存有服务端的证书拷贝,这里验证分两步,第一步验证证书的域名/有效期等信息,第二步是对比服务端返回的证书跟客户端返回的是否一致。需要考虑证书过期的问题,如果过期了,要想办法让app发起一个http请求,将续费的证书下载到沙盒中就可以了。

AFSSLPinningModePublicKey:这个模式同样是用证书绑定方式验证,客户端要有服务端的证书拷贝,只是验证时只验证证书里的公钥,不验证证书的有效期等信息。不需要考虑证书过期

我们来回顾一下AFSecurityPolicy的本地证书验证和我们之前提的https请求本地验证有什么不同。

https本地验证:是否权威机构的证书、域名是否一致、证书编号是否一致,都一致的话,就可以拿到服务端的公钥

AFSecurityPolicy验证:是否权威机构证书、域名是否一致(如果不验证域名,则忽略)、公钥验证或者证书验证

绿色的部分就是有差异的地方,其实证书编号是否一致苹果在底层已经做了。

如果选择AFSSLPinningModeNone,则两者是基本一致的,这也是默认策略。

但是如果选择其他两种,就表示在app内部放置了服务端的公钥证书(因为一般app请求的域名不会有太多,一般都是一个),这样的话就需要比较公钥证书或者公钥本身了,所以会多出来一步。但是这样做更加安全,对于防范中间人攻击更有效,回顾一下本文https部分应该比较容易理解。

AFSecurityPolicy的参考文章:

https://blog.csdn.net/u011374318/article/details/79364995

https://www.cnblogs.com/oc-bowen/p/5896041.html

最后,当我们通过了解https的请求过程,了解相关知识,了解如何防范中间人攻击,绕了这么大一圈后,再来理解AFSecurityPolicy,会发觉容易很多。

iOS之AFSecurityPolicy的更多相关文章

  1. IOS开发基础知识--碎片51

    1:https关闭证书跟域名的验证 AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy]; securityPolic ...

  2. 通读AFN③--HTTPS访问控制(AFSecurityPolicy),Reachability(AFNetworkReachabilityManager)

    这一篇主要介绍使用AFN如何访问HTTPS网站以及这些做法的实现原理,还有介绍AFN的网络状态监测部分AFNetworkReachabilityManager,这个模块会和苹果官方推荐的Reachab ...

  3. iOS 支持 IPv6

    苹果的规定:2016年6月1日提交到App Store必须支持IPv6-only网络. 官方文档:https://developer.apple.com/library/mac/documentati ...

  4. AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy

    在我们平时的开发中,对网络连接安全方面所做的努力,应该占据很重要的位置. 在解释AFSecurityPolicy之前,我们先把基础的http/https 知识简单的普及一下.获取这方面的信息可通过这本 ...

  5. 李洪强iOS经典面试题142-第三方框架及其管理

    李洪强iOS经典面试题142-第三方框架及其管理   第三方框架及其管理 使用过CocoaPods吗?它是什么?CocoaPods的原理? CocoaPod是一个第三方库的管理工具,用来管理项目中的第 ...

  6. ios https适配(单向验证)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https是http+tls.是在http和tcp之间添加了一层ssl加密验证,ssl将http发送的信息在将要发到传输层时进行了加密,同样数据从 ...

  7. iOS支持Https

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

  8. iOS常用网络库之AFNetWorking

    简介 ​     `AFNetworking`是iOS开发网络API中最常用的第三方库,`github`中的`star`数充分说明了它在iOS开发中第三方库中的江湖地位  github地址:[AFNe ...

  9. iOS 适配https

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

随机推荐

  1. ELK日志收集平台部署

    需求背景 由于公司的后台服务有三台,每当后台服务运行异常,需要看日志排查错误的时候,都必须开启3个ssh窗口进行查看,研发们觉得很不方便,于是便有了统一日志收集与查看的需求. 这里,我用ELK集群,通 ...

  2. 怎样把Linux的私钥文件id_rsa转换成putty的ppk格式

    在Linux VPS下产生的私钥文件putty是不认识的,putty只认识自己的ppk格式,要在这两种格式之间转换,需要PuTTYgen这个程序. puttygen是putty的配套程序,putty的 ...

  3. 配置COCO API(安装COCO)

    仍旧是win10,Python3.5 从GitHub下载coco源码,解压到任意文件夹.(或者创建一个工程)coco源码链接 https://github.com/cocodataset/cocoap ...

  4. JVM内存管理概述与android内存泄露分析

    一.内存划分 将内存划分为六大部分,分别是PC寄存器.JAVA虚拟机栈.JAVA堆.方法区.运行时常量池以及本地方法栈. 1.PC寄存器(线程独有):全称是程序计数寄存器,它记载着每一个线程当前运行的 ...

  5. Android开发心得-使用File ExPlorer无法访问系统内部文件

    问题:本机在获得ROOT权限后,使用Eclipse自带的File Explorer访问/data/data下各APP的存储文件,均无法打开.更换另外一个设备后,情况正常.Sumsung的有些机型在获得 ...

  6. Android游戏开发之旅 View类详解

    Android游戏开发之旅 View类详解 自定义 View的常用方法: onFinishInflate() 当View中所有的子控件 均被映射成xml后触发 onMeasure(int, int) ...

  7. python全栈学习--day10(函数进阶)

    一,引言 现在我有个问题,函数里面的变量,在函数外面能直接引用么? def func1(): m = 1 print(m) print(m) #这行报的错 报错了:NameError: name 'm ...

  8. 使用idea新建jsp

    使用idea解决新建jsp文件而找不到jsp文件模版的新建选项,这样每次创建一个新的jsp文件岂不是很耗时间? 解决办法: 就是要让idea知道你需要在这个目录下创建jsp文件 左上角,file中点击 ...

  9. 201621123057 《Java程序设计》第11周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 1. 源代码阅读:多线程程序BounceThread 1.1 BallR ...

  10. swift 编写欢迎界面-- ios开发

    转载自:http://blog.csdn.net/u014455765/article/details/49622947 现在很多iOS开发人员都从oc转向Swift, swift 也必将成为ios开 ...