1引言:公司的接口一般会两种协议的,一种HTTP,一种HTTPS的,HTTP 只要请求,服务器就会响应,如果我们不对请求和响应做出加密处理,所有信息都是会被检测劫持到的,是很不安全的,客户端加密可以使用本文这套工具类进行处理。但是不论在任何时候,都应该将服务置于HTTPS上,因为它可以避免中间人攻击的问题,还自带了基于非对称密钥的加密通道。
HTTPS交互原理
简答说,HTTPS 就是 HTTP协议加了一层SSL协议的加密处理,SSL 证书就是遵守 SSL协议,由受信任的数字证书颁发机构CA(如GlobalSign,wosign),在验证服务器身份后颁发,这是需要花钱滴,签发后的证书作为公钥一般放在服务器的根目录下,便于客户端请求返回给客户端,私钥在服务器的内部中心保存,用于解密公钥。
HTTPS 客户端与服务器交互过程:
1)客户端发送请求,服务器返回公钥给客户端;
2)客户端生成对称加密秘钥,用公钥对其进行加密后,返回给服务器;
3)服务器收到后,利用私钥解开得到对称加密秘钥,保存;
4)之后的交互都使用对称加密后的数据进行交互。
证书
简单说,证书有两种,一种是正经的:
CA颁发的证书
一种是不正经的:
自己生成签发的证书
我们需要做什么
如果遇到正经的证书,我们直接用AFNetworking 直接请求就好了,AFNetworking 内部帮我们封装了HTTPS的请求方式,但是大部分公司接口都是不正经的证书,这时需要我们做以下几步:
1)将服务器的公钥证书拖到Xcode中
2)修改验证模式
manager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];
原理
简单来说,就是你本可以修改AFN这个设置来允许客户端接收服务器的任何证书,但是这么做有个问题,就是你无法验证证书是否是你的服务器后端的证书,给中间人攻击,即通过重定向路由来分析伪造你的服务器端打开了大门。
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
securityPolicy.allowInvalidCertificates = YES;
解决方法
AFNetworking是允许内嵌证书的,通过内嵌证书,AFNetworking就通过比对服务器端证书、内嵌的证书、站点域名是否一致来验证连接的服务器是否正确。由于CA证书验证是通过站点域名进行验证的,如果你的服务器后端有绑定的域名,这是最方便的。将你的服务器端证书,如果是pem格式的,用下面的命令转成cer格式
openssl x509 -in <你的服务器证书>.pem -outform der -out server.cer
然后将生成的server.cer文件,如果有自建ca,再加上ca的cer格式证书,引入到app的bundle里,AFNetworking在
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy AFSSLPinningModeCertificate];
或者
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy AFSSLPinningModePublicKey];
情况下,会自动扫描bundle中.cer的文件,并引入,这样就可以通过自签证书来验证服务器唯一性了。
AFSecurityPolicy三种验证模式
1)AFSSLPinningModeNone 这个模式表示不做SSL pinning, 只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书。若证书是信任机构签发的就会通过,若是自己服务器生成的证书就不会通过。
2)AFSSLPinningModeCertificate 这个模式表示用证书绑定方式验证证书,需要客户端保存有服务端的证书拷贝,这里验证分两步,第一步验证证书的域名有效期等信息,第二步是对比服务端返回的证书跟客户端返回的是否一致。
3)AFSSLPinningModePublicKey 这个模式同样是用证书绑定方式验证,客户端要有服务端的证书拷贝, 只是验证时只验证证书里的公钥,不验证证书的有效期等信息。只要公钥是正确的,就能保证通信不会被窃听,因为中间人没有私钥,无法解开通过公钥加密的数据。
转载自:http://www.devashen.com/blog/2016/08/05/https/
1. 客户端设置实例
- 服务器:权威ca颁发的证书(非权威机构颁发的证书,或自签名的证书),申请ca时的私钥
- 申请者:csr请求文件,私钥(csr制作时一并随机产生的)
- 客户端:指浏览器/APP, 绑定公钥证书, 或者使用根证书
根据证书颁发机构权威性分情况介绍如下:
1)服务器返回https权威ca证书,浏览器会搜寻自带安装的权威ca根证书列表,自动验证ca公钥证书。
2)服务器返回自签名证书,无法自动验证,需要导入自签名根证书验证ca公钥证书,使用指定的根证书进行验证。
3)服务器返回自签名证书,手动点击信任风险,不验证证书的有效性、是否篡改、是否是其它ca机构签名的签名证书。 =》 BS架构适用
这种情况只有手机浏览器访问api,才会弹出验证。app客户端通讯库不会弹出。如果不加忽略处理,直接请求不到数据。
4)服务器返回自签名证书,app把自签名根证书手动安装,设置-》通用-》设备管理,可以查看信任证书列表。信任后,跟1)的情况就相同了。
a) 不验证:把根证书导入系统根证书列表。-- 只验证是否在信任列表中
b) 公钥绑定:把公钥证书放在app中,把服务器返回的证书,和提前放的公钥证书进行比较,是否一致 ---服务端证书与客户端证书公钥是否一致
c)证书绑定:把公钥证书放在app中,把服务器返回的证书,和提前放的公钥证书进行比较,是否一致 ---该模式会验证证书是否在信任列表中,然后再对比服务端证书和客户端证书是否一致
2. 证书种类
3.ssl握手
4.相关加密算法
非对称加密算法:RSA,DSA/DSS
对称加密算法:AES,RC4,3DES
HASH算法:MD5,SHA1,SHA256
- [阿里云 证书服务][1]
- [阿里云 移动安全][2]
[1]: https://help.aliyun.com/knowledge_list/42212.html?spm=5176.7742228.0.0.sLpeDT
[2]: https://help.aliyun.com/document_detail/35016.html?spm=5176.product34401.6.102.Nyqfl5
[3]: https://help.aliyun.com/knowledge_list/42212.html?spm=5176.7742228.0.0.sLpeDT "ff"
1, 获取服务端的证书
不需要服务端的人发给我们,我们自己就可以获取
openssl s_client -connect xxx.api.cn:443 </dev/null 2>/dev/null | openssl x509 -outform DER > https.cer
2, afnetwork配置
把上面步骤的.cer文件拖拽到项目中, 确保Add to targets被选中
AFHttpSessionMananger设置如下:
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
manager.securityPolicy = policy;
3,ATS配置
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>xx.api.cn</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSThirdPartyExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
</dict>
</dict>
</dict>
如果报错:nw_coretls_read_one_record tls_handshake_process: [-9801]
基本上就是服务器TLSv1.0 版本问题, 苹果要求 默认是TLS 1.2版本 256位加密算法。
服务器的人通常都很忙, 因此我们自己来兼容, 这才有了如上设置NSExceptionRequiresForwardSecrecy, NSExceptionMinimumTLSVersion
好了,到此,设置完毕,可以看到无法抓包了
4, 分析ATS
1、所有网址都可以http访问,允许任意链接NSAllowsArbitraryLoads为true
2、所有网址都不可以http访问, 允许任意链接NSAllowsArbitraryLoads为false
全部要https
3、所有网址都可以http访问(NSAllowsArbitraryLoads 为true)
设置一些例外,NSExceptionAllowsInsecureHttpLoads 为false, 不允许http访问。 true允许http访问
4、所有网址都不可以http访问(NSAllowsArbitraryLoads 为false),
设置一些例外,NSExceptionAllowsInsecureHttpLoads 为false, 不允许http访问。 true允许http访问
我们的需求是:
1:访问xxx.api.cn需要证书验证,抓包无法看明文数据。
2:访问其他http接口或是页面,不验证,自由访问
NSAppTransportSecurity //一个用于配置App Transport Security行为的属性,在Info.plist中是于Bundle Identifier同一级别的属性
NSAllowsArbitraryLoads //一个用于针对不在NSExceptionDomains中的配置项。如果设置成YES,则对于那些不在NSExceptionDomains的域则不需要通过ATS的验证。默认值是No。
NSExceptionDomains //用于配置例外的配置项,即在该配置项中的域不需要通过ATS的验证。
< domain-name-for-exception-as-string > //需要添加例外的域名字符串,如:www.baidu.com
NSExceptionMinimumTLSVersion //用于指定例外域名的TSL的版本号,可用的配置有TLSv1.0、TLSv1.1以及TLSv1.2三个配置项。
NSExceptionRequiresForwardSecrecy //用于指定所配置的域协议是否在ATS的所要求的第三点中。如果是YES,则说明所配置的域符合要求的第三点,如果是NO, 则加密算法必须是以下这几种。默认值是YES。
NSExceptionAllowsInsecureHTTPLoads //用于指明所配置的域是否个HTTPS的服务器。用这个配置可用访问那些没有证书、自签名证书、过期证书以及证书与域名匹配不上的服务器。默认值是NO。
NSIncludesSubdomains //用于指明子域名是否使用同样的配置。默认值是NO。
NSThirdPartyExceptionMinimumTLSVersion //该变量在如果是域名为第三的域名,且开发人员无法控制的情况下进行配置。
NSThirdPartyExceptionRequiresForwardSecrecy //该变量在如果是域名为第三的域名,且开发人员无法控制的情况下进行配置。
NSThirdPartyExceptionAllowsInsecureHTTPLoads //该变量在如果是域名为第三的域名,且开发人员无法控制的情况下进行配置。
5, 参考
http://www.cnblogs.com/dsxniubility/p/4821184.html
http://www.pluto-y.com/ios9-nsapptransportsecurity/
https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html
//www.greatytc.com/p/4102b817ff2f