iOS https 切换的探究

1,740 阅读6分钟
原文链接: www.jianshu.com

背景

谷歌从 2017 年起,Chrome 浏览器将也会把采用 HTTP 协议的网站标记为「不安全」网站;苹果从 2017 年 iOS App 将强制使用 HTTPS;在国内热火朝天的小程序也要求必须使用 HTTPS 请求。

1: 什么是HTTPS 

在说HTTPS之前先说说什么是HTTP,HTTP就是我们平时浏览网页时候使用的一种协议。HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全。为了保证这些隐私数据能加密传输,SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。SSL 3.0进行了升级,于是出现了TLS(Transport Layer Security)。目前苹果要求原文:The protocol Transport Security Layer (TLS) must be at least version 1.2。

2: HTTPS原理



原理图



可以参考这份资料:https握手流程。 里面介绍了单向双向加密流程以及区别自行查看。

3: 什么样的证书满足条件呢?

第一种是创建证书请求,然后到权威机构认证,随之配置到服务器;

第二种是自建证书,需要自己配置给服务器。

看文档描述:

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)必须至少1.2版:看下图谷歌的证书信息。



按照图标的指向点击


接的加密方式需要提供“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




看Google证书算法信息箭头指向地方

但是也要注意证书的合法性,注意是否有效,iOS要求连接的HTTPS站点必须为CA签名过的合法证书。

看几个不符合条件证书的例子:



这个是自己创建的证书没有被验证



这个证书不被信任



证书有效


AFN 可以通过allowInvalidCertificates 来针对以上情况设置

allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO//如果是需要验证自建证书,需要设置为YES

我们可以自己用命令验证下证书合法性。

下面这个都是Fail


以上是自己自建的证书,不会第三方信任的


看一个没问题的证书:



这个是我们自己服务器验证结果


4:自建证书的流程

//第一步,为服务器端和客户端准备公钥、私钥

# 生成服务器端私钥

openssl genrsa -outserver.key1024

# 生成服务器端公钥

openssl rsa -inserver.key -pubout -outserver.pem

//第二步,生成 CA 证书

# 生成 CA 私钥

openssl genrsa -outca.key1024

# X.509 Certificate Signing Request (CSR) Management.

openssl req -new-key ca.key -outca.csr

# X.509 Certificate Data Management.

openssl x509 -req -inca.csr -signkey ca.key -outca.crt

//第三步,生成服务器端证书

# 服务器端需要向 CA 机构申请签名证书,在申请签名证书之前依然是创建自己的 CSR 文件

openssl req -new -key server.key-outserver.csr

# 向自己的 CA 机构申请证书,签名过程需要 CA 的证书和私钥参与,最终颁发一个带有 CA 签名的证书

openssl x509 -req -CA ca.crt-CAkeyca.key-CAcreateserial-inserver.csr-outserver.cr

第四步,生成cer文件

使用openssl 进行转换

openssl x509 -inserver.crt-outserver.cer-outform der

如果对以上命令不是太熟悉可以查下。或者看下图。



常用的帮助信息

可以用命令看看自己生成的一些东西一般在/Users/自己名字下面  ls 看列出来的如下图:



这些就是上面生成的文件信息列表

5:把证书配置到服务器

我用的XAMPP 本机搭建的服务器如下图:



启动所有服务

注意:低版本的Mac OS 系统,Mysql 可能不能自动启动,需要用命令自己启动 : sudo /Applications/XAMPP/xamppfiles/bin/mysql.serverstart

配置修改httpd-ssl.conf文件 把server.crt和server.key的路径修改对就好了 由于我的服务器默认开启ssl,因此我就修改下证书路径就好了。


由于XAMPP自己有了我就不配置了

来在浏览器看下结果


这个是QQ浏览器


Google比较严格看下图:



因此证书需要必须为CA签名过的合法证书


以上是自建证书的流程可以自己玩玩。


6:创建证书请求,然后到权威机构认证,随之配置到服务器

由于我们服务器已经有了,我给后台要一直没反应,没给我,我就自己获取了通过命令 。我们可以使用以下openssl命令来获取到服务器的公开二进制证书。

openssl s_client -connect www.example.com.cn:443 /dev/null | openssl x509 -outform DER > https.cer



搜索找到

7:iOS客户端的适配

单向认证 SSL 协议不需要客户拥有 CA 证书,只需将服务器端验证客户证书的过程去掉。双向认证这种情况要求服务器和用户双方都有证书。

[self.sessionManagersetSecurityPolicy:[NKNetWorkCentercustomSecurityPolicy]];

+ (AFSecurityPolicy*)customSecurityPolicy

{

// /先导入证书

NSString*cerPath = [[NSBundlemainBundle]pathForResource:@"https"ofType:@"cer"];//证书的路径

NSData*certData = [NSDatadataWithContentsOfFile:cerPath];

// AFSSLPinningModeCertificate使用证书验证模式

AFSecurityPolicy*securityPolicy = [AFSecurityPolicypolicyWithPinningMode:AFSSLPinningModeCertificate];

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

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

securityPolicy.allowInvalidCertificates=YES;

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

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

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

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

securityPolicy.validatesDomainName=NO;

securityPolicy.pinnedCertificates=@[certData];

returnsecurityPolicy;

}

下面是用花瓶抓包的结果


加密的

8:对于一些第三方http请求的处理

要么他们升级。要么加入ATS 白名单里。