【加密解密】HTTPS

学习文章

概念释疑

为了强制增强数据访问安全,iOS9 默认会把所有从 NSURLConnectionCFURLNSURLSession发出的 HTTP请求,都改为 HTTPS 请求:iOS9.x-SDK编译时,默认会让所有从NSURLConnectionCFURLNSURLSession发出的 HTTP 请求统一采用TLS 1.2 协议。因为 AFNetworking 现在的版本底层使用了 NSURLConnection ,众多App将被影响(基于iOS8.x-SDK的App不受影响)。服务器因此需要更新,以解析相关数据。而这一做法,官方文档称为ATS,全称为App Transport Security,是iOS9的一个新特性。如不更新,可通过在 Info.plist 中声明,倒退回不安全的网络请求。
一个符合 ATS 要求的 HTTPS,应该满足如下条件:

  • Transport Layer Security协议版本要求TLS1.2以上
  • 服务的Ciphers配置要求支持Forward Secrecy等
  • 证书签名算法符合ATS要求等

注: ATS只信任知名CA颁发的证书,小公司所使用的 self signed certificate,还是会被ATS拦截。

什么是SSL/TLS?跟HTTP和HTTPS有什么关系?

什么是SSL/TLS? SSL你一定知道,在此不做赘述。主要说下什么是TLS,还有跟HTTP和HTTPS有什么关系。
TLS 是 SSL 新的别称:
“TLS1.0”之于“SSL3.1”,犹“公元2015”之于“民国104”,“一千克”之于“一公斤”:称呼不同,意思相同。
SSL 3.0版本之后的迭代版本被重新命名为TLS 1.0:TLS 1.0=SSL 3.1。所以我们平常也经常见到 “SSL/TLS” 这种说法。
目前,应用最广泛的是TLS 1.0,接下来是SSL 3.0。目前主流浏览器都已经实现了TLS 1.2的支持。
常用的有下面这些:

  • SSL 2.0
  • SSL 3.0
  • TLS 1.0 (SSL 3.1)
  • TLS 1.1 (SSL 3.1)
  • TLS 1.2 (SSL 3.1)
    那为什么标题是“使用HTTPS”而没有提及SSL和TLS什么事? “SSL/TLS”跟HTTP和HTTPS有什么关系?
    要理解这个,要看下他们之间的关系:
    HTTP+SSL/TLS+TCP = HTTPS

HTTPS.jpg

或者
HTTPS = “HTTP over SSL”

也就是说

Apple让你的HTTP采用SSL/TLS协议,就是让你从HTTP转到HTTPS。而这一做法,官方文档称为ATS,全称为App Transport Security。

为什么要用SSL/TLS?

不使用SSL/TLS的HTTP通信,就是不加密的通信!

不使用SSL/TLS的HTTP通信,所有信息明文传播,带来了三大风险:

  • 窃听风险(eavesdropping):第三方可以获知通信内容。
  • 篡改风险(tampering):第三方可以修改通信内容。
  • 冒充风险(pretending):第三方可以冒充他人身份参与通信。

SSL/TLS协议是为了解决这三大风险而设计的,希望达到:

  • 所有信息都是加密传播,第三方无法窃听。
  • 具有校验机制,一旦被篡改,通信双方会立刻发现。
  • 配备身份证书,防止身份被冒充。

SSL/TLS的作用,打个比方来讲:

如果原来的 HTTP 是塑料水管,容易被戳破;那么如今新设计的 HTTPS 就像是在原有的塑料水管之外,再包一层金属水管(SSL/TLS协议)。一来,原有的塑料水管照样运行;二来,用金属加固了之后,不容易被戳破。

解决方案

符合ATS

如果所有请求接口都符合ATS,那么,对于前端来说,则不需要做任何额外工作.

不完全符合ATS

这是很有可能的,因为即使公司的正式接口符合ATS,开发中,还是需要测试接口的.我们很有可能没有时间金钱,使测试接口也符合ATS,也没必要.

对于不完全符合ATS的情况,我们可以通过调整plist来轻松做到.

Exception.png

正如在上图中看到的,苹果官方提供了一些可选配置项来决定是否开启ATS模式.

开发者可以针对某些确定的URL不使用ATS,这需要在工程中的info.plist中标记NSExceptionDomains。在NSExceptionDomains字典中,可以显式的指定一些不使用ATS的URL.键值如下:

  • NSIncludesSubdomains
  • NSExceptionAllowInsecureHTTPLoads
  • NSExceptionRequiresForwardSecrecy
  • NSExceptionMinimumTLSVersion
  • NSThirdPartyExceptionAllowsInsecureHTTPLoads
  • NSThirdPartyExceptionMinimumTLSVersion
  • NSThirdPartyExceptionRequiresForwardSecrecy

关于App Transport Security,每个应用都属于4个大类当中的一类。我们来看看每一个大类都是怎样影响应用的。

-- 分类名 解释
1. HTTPS Only (只有HTTPS,所有情况下都使用ATS) 如果你的应用只基于支持HTTPS的服务器,那么你太幸运了。你的应用不需要做任何改变。但是,注意App Transport Security要求TLS 1.2而且它要求站点使用支持forward secrecy协议的密码。证书也要求是符合ATS规格的。因此慎重检查与你的应用交互的服务器是不是符合ATS的要求非常重要。
2. Mix & Match(混合) 你的应用与一个不符合ATS要求的服务器工作是很有可能的。在这种情况下,你需要告诉操作系统哪些站点是涉及到的然后在你的应用的 Info.plist文件中指明哪些要求没有达到。
3. Opt Out(禁用ATS) 如果你在创建一个网页浏览器,那么你有一个更大的麻烦。因为你不可能知道你的用户将要访问那个网页,你不可能指明这些网页是否支持ATS要求且在HTTPS上传输。在这种情况下,除了全部撤销 App Transport Security 没有其它办法。
4. Opt Out With Exceptions(除特殊情况外,都不使用ATS) 当你的应用撤消了App Transport Security,,但同时定义了一些例外。这非常有用就是当你的应用从很多的服务器上取数据,但是也要与一个你可控的API交互。在这种情况下,在应用的Info.plist文件中指定任何加载都是被允许的,但是你也指定了一个或多个例外来表明哪些是必须要求 App Transport Security的。

根据上表,分别做一下解释

1.HTTPS Only (只有HTTPS,所有情况下都使用ATS

如果你的应用只基于支持HTTPS的服务器,那么你太幸运了。你的应用不需要做任何改变。

2.Mix & Match(混合)

你的应用与一个不符合ATS要求的服务器工作是很有可能的.

我们拿三个域名来举例子:

1.api.insecuredomain.com

我们要让这个域名的请求使用http

Info.plist 配置中的XML源码如下所示:

  <key>NSAppTransportSecurity</key>
  <dict>
      <key>NSExceptionDomains</key>
      <dict>
          <key>api.insecuredomain.com</key>
          <dict>

              <!--允许App进行不安全的HTTP请求-->
              <key>NSExceptionAllowsInsecureHTTPLoads</key>
              <true/>

              <!--适用于这个特定域名下的所有子域-->
              <key>NSIncludesSubdomains</key>
              <true/>
          </dict>
      </dict>
  </dict>  

在 plist 文件里显示如下:

NSExceptionAllowsInsecureHTTPLoads.png

我们定义的第一个“例外”(Exception)告诉ATS当与这个子域交互的时候撤销了必须使用HTTPS的要求。注意这个仅仅针对在“例外”(Exception)中声明了的子域。非常重要的一点是要理解NSExceptionAllowsInsecureHTTPLoads关键字并不仅仅只是与使用HTTPS相关。这个“例外”(Exception)指明了对于那个域名,所有的App Transport Security的要求都被撤销了。

2.cdn.domain.com

我们要让这个域名使用低于TLS1.2的加密协议

Info.plist 配置中的XML源码如下所示:

   <key>NSAppTransportSecurity</key>
  <dict>
      <key>NSExceptionDomains</key>
      <dict>
          <key>cdn.somedomain.com</key>
          <dict>
              <key>NSThirdPartyExceptionMinimumTLSVersion</key>
              <string>TLSv1.1</string>
          </dict>
      </dict>
  </dict>  

在 plist 文件里显示如下:

NSThirdPartyExceptionMinimumTLSVersion.png

很可能你的应用是与一个支持HTTPS传输数据的服务器交互,但是并没有使用TLS 1.2或更高(上例中为TLS1.1)。在这种情况下,你定义一个“例外”(Exception),它指明应该使用的最小的TLS的版本。这比完全撤销那个域名的App Transport Security要更好更安全。

3.thatotherdomain.com

我们要让这个域名不支持ForwardSecrecy,同时,综合一下上面的用法

Info.plist 配置中的XML源码如下所示:

     <key>NSAppTransportSecurity</key>
      <dict>
          <key>NSExceptionDomains</key>
          <dict>
              <key>thatotherdomain.com</key>
              <dict>
                  <!--适用于这个特定域名下的所有子域-->
                  <key>NSIncludesSubdomains</key>
                  <true/>
                  <!--扩展可接受的密码列表:这个域名可以使用不支持 forward secrecy 协议的密码-->
                  <key>NSExceptionRequiresForwardSecrecy</key>
                  <false/>
                  <!--允许App进行不安全的HTTP请求-->
                  <key>NSExceptionAllowsInsecureHTTPLoads</key>
                  <true/>
                  <!--在这里声明所支持的 TLS 最低版本-->
                  <key>NSExceptionMinimumTLSVersion</key>
                  <string>TLSv1.1</string>
              </dict>
          </dict>
      </dict>  

在 plist 文件里显示如下:

NSExceptionRequiresForwardSecrecy.png

NSIncludesSubdomains 关键字告诉 App Transport Security 这个“例外”(Exception)适用于这个特定域名的所有子域。这个“例外”(Exception)还进一步通过扩展可接受的密码列表来定义这个域名可以使用不支持forward secrecy( NSExceptionRequiresForwardSecrecy ) 协议的密码.

如果你的App中同时用到了这三个域名,那么应该是这样:

     <key>NSAppTransportSecurity</key>
        <dict>
            <key>NSExceptionDomains</key>
            <dict>
                <key>api.insecuredomain.com</key>
                <dict>
                    <key>NSExceptionAllowsInsecureHTTPLoads</key>
                    <false/>
                </dict>
                <key>cdn.somedomain.com</key>
                <dict>
                    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
                    <string>TLSv1.1</string>
                </dict>
                <key>thatotherdomain.com</key>
                <dict>
                    <key>NSIncludesSubdomains</key>
                    <true/>
                    <key>NSExceptionRequiresForwardSecrecy</key>
                    <false/>
                </dict>
            </dict>
        </dict>  

在 plist 文件里显示如下:

综合.png

3. Opt Out(禁用ATS)

上面是比较严谨的做法,指定了能访问哪些特定的HTTP。当然也有暴力的做法: 彻底倒退回不安全的HTTP网络请求,能任意进行HTTP请求,比如你在开发一款浏览器App,或者你想偷懒,或者后台想偷懒,或者公司不给你升级服务器...

你可以在Info.plist 配置中改用下面的XML源码:

    <key>NSAppTransportSecurity</key>
    <dict>
        <!--彻底倒退回不安全的HTTP网络请求,能任意进行HTTP请求 (不建议这样做)-->
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>  

在 plist 文件里显示如下:

禁用ATS.png

4. Opt Out With Exceptions(除特殊情况外,都不使用ATS)

上面已经介绍了三种情景,还有一种可能你也会遇到:

当你的应用撤消了App Transport Security,,但同时定义了一些“例外”(Exception)。当你的应用从很多的服务器上取数据,但是也要与一个你可控的API交互。在这种情况下,在应用的Info.plist文件中指定任何加载都是被允许的,但是你也指定了一个或多个“例外”(Exception)来表明哪些是必须要求 App Transport Security的。下面是Info.plist文件应该会有的内容:

<key>NSAppTransportSecurity</key>
        <dict>
            <key>NSAllowsArbitraryLoads</key>
            <true/>
            <key>NSExceptionDomains</key>
            <dict>
                <key>api.tutsplus.com</key>
                <dict>
                    <key>NSExceptionAllowsInsecureHTTPLoads</key>
                    <false/>
                </dict>
            </dict>
        </dict>  

在 plist 文件里显示如下:

除特殊情况外,都不使用ATS.png

Certificate Transparency

虽然ATS大多数安全特性都是默认可用的,Certificate Transparency 是必须设置的。如果你有支持Certificate Transparency的证书,你可以检查NSRequiresCertificateTransparency关键字来使用Certificate Transparency。再次强调,如果你的证书不支持Certificate Transparency,此项需要设置为不可用。

如果需要调试一些由于采用了ATS而产生的问题,需要设置CFNETWORK_DIAGNOSTICS为1,这样就会打印出包含被访问的URL和ATS错误在内的NSURLSession错误信息。要确保处理了遇到的所有的错误消息,这样才能使ATS易于提高可靠性和扩展性。

说明

  1. 在使用Xcode7,并且Base SDK用的iOS9及以上版本,才需要网络适配ATS。
  2. 在OS X EI Capitan系统的终端中通过nscurl命令来诊断检查你的HTTPS服务配置是否满足Apple的ATS要求:
 $ nscurl --verbose --ats-diagnostics https://<your_server_domain>  

Demo

我们写一个Demo来说明会更清晰.

我们用https://www.apple.com/来说明符合ATS的请求,这个不需要我们做任何额外工作,可以直接写请求,并得到数据.

我们用https://kyfw.12306.cn/来说明不完全符合ATS的请求,12306的证书并没有经过知名CA认证(这就相当于我们后台自签名证书,没有经过知名CA认证).

首先,我们需要得到证书,12306的证书需要我们从其网站下载(火狐浏览器).

访问12306.png
点击View.png
导出证书.png
存储为第一种格式.png
命令行生成cer.png

把pem转化为cer的命令为(我的文件取和存的地址都在桌面):

$ openssl x509 -inform PEM -in ~/Desktop/kyfw.12306.cn.pem -outform DER -out ~/Desktop/kyfw_12306_cn.cer  
由pem得到cer.png

然后,把cer导入到工程中,记得勾选我们使用cer所对应的target,否则可能会出bug.

将cer导入到工程.png

由于我们是本地导入的证书,所以我们要识别证书,这需要做一些验证,还好AFNetworking极大的简化了这个步骤.

    manager.securityPolicy      = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    manager.securityPolicy.validatesDomainName      = YES;
    manager.securityPolicy.allowInvalidCertificates = YES;  

如果是使用NSURLConnection,NSURLSession或者其他网络框架,就需要自己写验证了,可以参考这篇文章.

(本人对于NSURLConnection,NSURLSession验证证书的代码没搞懂,所以抱歉,这里就没法给出原生网络请求验证的Demo了- - !)

接着,按照上面的教程调整plist文件,对于12306的证书,我们不太清楚它具体那里没有符合ATS,我们需要尝试.

最终,这样可以调通:

设置12306证书的Exception.png

源码:

#import "ViewController.h"
#import "AFNetworking.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self networking];
}

- (void)networking {
    
    NSString * appleStr = @"https://www.apple.com/";
    
    NSString * kyfwStr = @"https://kyfw.12306.cn/otn/";
    
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    
    // 使用自签名证书要用以下代码来验证,其他情况要注掉
//    manager.securityPolicy      = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
//    manager.securityPolicy.validatesDomainName      = YES;
//    manager.securityPolicy.allowInvalidCertificates = YES;
    
    [manager GET:appleStr parameters:nil success:^(AFHTTPRequestOperation * _Nonnull operation, id  _Nonnull responseObject) {
        
        NSLog(@"****JSON: %@", responseObject);
        
    } failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error) {
        
        NSLog(@"****Error: %@", error);
    }];
}

@end  

以上,我们详细列举了iOS实现https的情况,虽然内容很多,但其实,在实际开发中,需要我们做的工作并不多.总结来说,如果我们的请求符合ATS,则我们不需要做任何额外工作,如果不完全符合,比如,自签名证书,需要我们把自签名证书导入工程,调整plist文件,验证该证书即可.

下载源码

下载地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容