AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy
在我们平时的开发中,对网络连接安全方面所做的努力,应该占据很重要的位置。
在解释AFSecurityPolicy之前,我们先把基础的http/https 知识简单的普及一下。获取这方面的信息可通过这本书:图解HTTP
HTTP:
1.HTTP协议用于客户端和服务器端之间的通信
2.通过请求和相应的交换达成通信
客户端请求:
服务器端响应:
3.HTTP是不保存状态的协议
HTTP自身不会对请求和相应之间的通信状态进行保存。什么意思呢?就是说,当有新的请求到来的时候,HTTP就会产生新的响应,对之前的请求和响应的保温信息不做任何存储。这也是为了快速的处理事务,保持良好的可伸展性而特意设计成这样的。
4.请求URI定位资源
URI算是一个位置的索引,这样就能很方便的访问到互联网上的各种资源。
5.告知服务器意图的HTTP方法
①GET: 直接访问URI识别的资源,也就是说根据URI来获取资源。
②POST: 用来传输实体的主体。
③PUT: 用来传输文件。
④HEAD: 用来获取报文首部,和GET方法差不多,只是响应部分不会返回主体内容。
⑤DELETE: 删除文件,和PUT恰恰相反。按照请求的URI来删除指定位置的资源。
⑥OPTIONS: 询问支持的方法,用来查询针对请求URI指定的资源支持的方法。
⑦TRACE: 追踪路径,返回服务器端之前的请求通信环信息。
⑧CONNECT: 要求用隧道协议连接代理,要求在与代理服务器通信时简历隧道,实现用隧道协议进行TCP通信。SSL(Secure Sockets Layer)和TLS(Transport Layer Security)就是把通信内容加密后进行隧道传输的。
6.管线化让服务器具备了相应多个请求的能力
7.Cookie让HTTP有迹可循
HTTP是一套很简单通信协议,因此也非常的高效。但是由于通信数据都是明文发送的,很容易被拦截后造成破坏。在互联网越来越发达的时代,对通信数据的安全要求也越来越高。
HTTPS
HTTPS是一个通信安全的解决方案,可以说相对已经非常安全。为什么它会是一个很安全的协议呢?下边会做出解释
大家可以看看这篇文章,解释的很有意思 。
HTTP + 加密 + 认证 + 完整性保护 = HTTPS
其实HTTPS是身披SSL外壳的HTTP,这句话怎么理解呢?
大家应该都知道HTTP是应用层的协议,但HTTPS并非是应用层的一种新协议,只是HTTP通信接口部分用SSL或TLS协议代替而已。
通常 HTTP 直接和TCP通信,当使用SSL时就不同了。要先和SSL通信,再由SSL和TCP通信。
这里再说一些关于加密的题外话:
现如今,通常加密和解密的算法都是公开的。举个例子: a * b = 200,加入a是你知道的密码,b是需要被加密的数据,200 是加密后的结果。那么这里这个*号就是一个很简单的加密算法。这个算法是如此简单。但是如果想要在不知道a和b其中一个的情况下进行破解也是很困难的。就算我们知道了200 然后得到a b 这个也很难。假设知道了密码a 那么b就很容易算出b = 200 / a 。
实际中的加密算法比这个要复杂的多。
介绍两种常用加密方法:
1.共享密钥加密
2.公开密钥加密
共享密钥加密就是加密和解密通用一个密钥,也称为对称加密。优点是加密解密速度快,缺点是一旦密钥泄露,别人也能解密数据。
公开密钥加密恰恰能解决共享密钥加密的困难,过程是这样的:
①发文方使用对方的公开密钥进行加密
②接受方在使用自己的私有密钥进行解密
关于公开密钥,也就是非对称加密 可以看看这篇文章 RSA算法原理
原理都是一样的,这个不同于刚才举得a和b的例子,就算知道了结果和公钥,破解出被机密的数据是非常难的。这里边主要涉及到了复杂的数学理论。
HTTPS采用混合加密机制
HTTPS采用共享密钥加密和公开密钥加密两者并用的混合加密机制。
好了我们大概已经知道了HTTPS是如何加密的了,那么这个相互认证过程是怎么样的呢 ?
在网上看到了这篇博客,http://blog.csdn.net/yuwuchaio/article/details/50469183 把他描述的剪切下来了
==================================================================
==================================================================
注意黄色的部分,这个指明了,我们平时使用的一个场景。这篇文章会很长,不仅仅是为了解释HTTPS,还为了能够增加记忆,当日后想看看的时候,就能通过读这边文章想起大部分的HTTPS的知识。下边解释一些更加详细的HTTPS过程。
好了HTTPS就说到着了,AFSecurityPolicy这个类其实就是为了验证证书是否正确
还是先看头文件里边有什么东西。要实现认证功能需要添加系统的Security,这个是必须的。
下边的这个枚举值的意思的是:
1. AFSSLPinningModeNone 代表无条件信任服务器的证书
2. AFSSLPinningModePublicKey 代表会对服务器返回的证书中的PublicKey进行验证,通过则通过,否则不通过
3. AFSSLPinningModeCertificate 代表会对服务器返回的证书同本地证书全部进行校验,通过则通过,否则不通过
说的是AFSecurityPolicy 用来评价通过X.509(数字证书的标准)的数字证书和公开密钥进行的安全网络连接是否值得信任。在应用内添加SSL证书能够有效的防止中间人的攻击和安全漏洞。强烈建议涉及用户敏感或隐私数据或金融信息的应用全部网络连接都采用使用SSL的HTTPS连接。
返回SSL Pinning的类型。默认的是AFSSLPinningModeNone。
这个属性保存着所有的可用做校验的证书的集合。AFNetworking默认会搜索工程中所有.cer的证书文件。如果想制定某些证书,可使用certificatesInBundle在目标路径下加载证书,然后调用policyWithPinningMode:withPinnedCertificates创建一个本类对象。
注意: 只要在证书集合中任何一个校验通过,evaluateServerTrust:forDomain: 就会返回true,即通过校验。
使用允许无效或过期的证书,默认是不允许。
是否验证证书中的域名domain
返回指定bundle中的证书。如果使用AFNetworking的证书验证 ,就必须实现此方法,并且使用policyWithPinningMode:withPinnedCertificates 方法来创建实例对象。
默认的实例对象,默认的认证设置为:
1. 不允许无效或过期的证书
2. 验证domain名称
3. 不对证书和公钥进行验证
这两个方法没什么好说的,都是创建security policy 的方法。
核心方法:使用起来是这样的,这个方法AFNetworking在内部调用了。这个后边会说到
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; AFSecurityPolicy *securityPolicy = [[AFSecurityPolicy alloc] init];
[securityPolicy setAllowInvalidCertificates:NO];
[securityPolicy setSSLPinningMode:AFSSLPinningModeCertificate];
[securityPolicy setValidatesDomainName:YES];
[securityPolicy setValidatesCertificateChain:NO]; manager.securityPolicy = securityPolicy;
好了本类的头文件已经看完了,接下来看.m
我们先看这个函数
// 在证书中获取公钥
static id AFPublicKeyForCertificate(NSData *certificate) {
id allowedPublicKey = nil;
SecCertificateRef allowedCertificate;
SecCertificateRef allowedCertificates[];
CFArrayRef tempCertificates = nil;
SecPolicyRef policy = nil;
SecTrustRef allowedTrust = nil;
SecTrustResultType result; // 1. 根据二进制的certificate生成SecCertificateRef类型的证书
// NSData *certificate 通过CoreFoundation (__bridge CFDataRef)转换成 CFDataRef
// 看下边的这个方法就可以知道需要传递参数的类型
/*
SecCertificateRef SecCertificateCreateWithData(CFAllocatorRef __nullable allocator,
CFDataRef data) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);
*/
allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); // 2.如果allowedCertificate为空,则执行标记_out后边的代码
__Require_Quiet(allowedCertificate != NULL, _out); // 3.给allowedCertificates赋值
allowedCertificates[] = allowedCertificate; // 4.新建CFArra: tempCertificates
tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, , NULL); // 5. 新建policy为X.509
policy = SecPolicyCreateBasicX509(); // 6.创建SecTrustRef对象,如果出错就跳到_out标记处
__Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out);
// 7.校验证书的过程,这个不是异步的。
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); // 8.在SecTrustRef对象中取出公钥
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); _out:
if (allowedTrust) {
CFRelease(allowedTrust);
} if (policy) {
CFRelease(policy);
} if (tempCertificates) {
CFRelease(tempCertificates);
} if (allowedCertificate) {
CFRelease(allowedCertificate);
} return allowedPublicKey;
}
在二进制的文件中获取公钥的过程是这样
① NSData *certificate -> CFDataRef -> (SecCertificateCreateWithData) -> SecCertificateRef allowedCertificate
②判断SecCertificateRef allowedCertificate 是不是空,如果为空,直接跳转到后边的代码
③allowedCertificate 保存在allowedCertificates数组中
④allowedCertificates -> (CFArrayCreate) -> SecCertificateRef allowedCertificates[1]
⑤根据函数SecPolicyCreateBasicX509() -> SecPolicyRef policy
⑥SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust) -> 生成SecTrustRef allowedTrust
⑦SecTrustEvaluate(allowedTrust, &result) 校验证书
⑧(__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust) -> 得到公钥id allowedPublicKey
这个过程我们平时也不怎么用,了解下就行了,真需要的时候知道去哪里找资料就行了。
这里边值得学习的地方是:
__Require_Quiet 和 __Require_noErr_Quiet 这两个宏定义。
我们看看他们内部是怎么定义的
可以看出这个宏的用途是:当条件返回false时,执行标记以后的代码
可以看出这个宏的用途是:当条件抛出异常时,执行标记以后的代码
这样就有很多使用场景了。当必须要对条件进行判断的时候,我们有下边几种方案了
1.#ifdef 这个是编译特性
2. if else 代码层次的判断
3 __Require_XXX 宏
_out 就是一个标记,这段代码__Require_Quiet 到_out之间的代码不会执行
再来看看下边的方法,主要是把key到出为NSData
这个方法是比较两个key是否相等,如果是ios/watch/tv直接使用isEqual方法就可进行比较。应为SecKeyRef本质上是一个struct,是不能直接用isEqual比较的,正好使用上边的那个方法把它转为NSData就可以了。
来看原文中这段解释
大概意思是分两种方式:下边的自定义的意思是,用户是否是自己主动设置信任的,比如有些弹窗,用户点击了信任
1.用户自定义的,成功是 kSecTrustResultProceed 失败是kSecTrustResultDeny
2.非用户定义的, 成功是kSecTrustResultUnspecified 失败是kSecTrustResultRecoverableTrustFailure
这就不难解释上边最后的那个或判断了。
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == )) {
// https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
// According to the docs, you should only trust your provided certs for evaluation.
// Pinned certificates are added to the trust. Without pinned certificates,
// there is nothing to evaluate against.
//
// From Apple Docs:
// "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
// Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;
} NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
} SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); // AFSSLPinningModeNone 不校验证书,
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
} // 代码能够走到这里说明两点
// 1.通过了根证书的验证
// 2.allowInvalidCertificates = YES switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: { // 全部校验
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
} // 把本地的证书设为根证书,即服务器应该信任的证书
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); // 校验能够信任
if (!AFServerTrustIsValid(serverTrust)) {
return NO;
} // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); // 判断本地证书和服务器证书是否相同
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
} return NO;
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = ;
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); // 找到相同的公钥就通过
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += ;
}
}
}
return trustedPublicKeyCount > ;
}
} return NO;
}
#pragma mark - NSKeyValueObserving + (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys {
return [NSSet setWithObject:@"pinnedCertificates"];
} #pragma mark - NSSecureCoding + (BOOL)supportsSecureCoding {
return YES;
} - (instancetype)initWithCoder:(NSCoder *)decoder { self = [self init];
if (!self) {
return nil;
} self.SSLPinningMode = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(SSLPinningMode))] unsignedIntegerValue];
self.allowInvalidCertificates = [decoder decodeBoolForKey:NSStringFromSelector(@selector(allowInvalidCertificates))];
self.validatesDomainName = [decoder decodeBoolForKey:NSStringFromSelector(@selector(validatesDomainName))];
self.pinnedCertificates = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(pinnedCertificates))]; return self;
} - (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:[NSNumber numberWithUnsignedInteger:self.SSLPinningMode] forKey:NSStringFromSelector(@selector(SSLPinningMode))];
[coder encodeBool:self.allowInvalidCertificates forKey:NSStringFromSelector(@selector(allowInvalidCertificates))];
[coder encodeBool:self.validatesDomainName forKey:NSStringFromSelector(@selector(validatesDomainName))];
[coder encodeObject:self.pinnedCertificates forKey:NSStringFromSelector(@selector(pinnedCertificates))];
} #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone {
AFSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init];
securityPolicy.SSLPinningMode = self.SSLPinningMode;
securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates;
securityPolicy.validatesDomainName = self.validatesDomainName;
securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone]; return securityPolicy;
}
好了,这篇就到这了,大体了解了SSL校验是怎么一回事了,而且知道了该如何操作。
在这里推荐一篇不错的文章 iOS 9之适配ATS
AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy的更多相关文章
- AFNetworking 3.0 源码解读(十一)之 UIButton/UIProgressView/UIWebView + AFNetworking
AFNetworking的源码解读马上就结束了,这一篇应该算是倒数第二篇,下一篇会是对AFNetworking中的技术点进行总结. 前言 上一篇我们总结了 UIActivityIndicatorVie ...
- AFNetworking 3.0 源码解读(十)之 UIActivityIndicatorView/UIRefreshControl/UIImageView + AFNetworking
我们应该看到过很多类似这样的例子:某个控件拥有加载网络图片的能力.但这究竟是怎么做到的呢?看完这篇文章就明白了. 前言 这篇我们会介绍 AFNetworking 中的3个UIKit中的分类.UIAct ...
- AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager
让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心. 前言 AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控 ...
- AFNetworking 3.0 源码解读(八)之 AFImageDownloader
AFImageDownloader 这个类对写DownloadManager有很大的借鉴意义.在平时的开发中,当我们使用UIImageView加载一个网络上的图片时,其原理就是把图片下载下来,然后再赋 ...
- AFNetworking 3.0 源码解读(七)之 AFAutoPurgingImageCache
这篇我们就要介绍AFAutoPurgingImageCache这个类了.这个类给了我们临时管理图片内存的能力. 前言 假如说我们要写一个通用的网络框架,除了必备的请求数据的方法外,必须提供一个下载器来 ...
- AFNetworking 3.0 源码解读(六)之 AFHTTPSessionManager
AFHTTPSessionManager相对来说比较好理解,代码也比较短.但却是我们平时可能使用最多的类. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilit ...
- AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization
这篇就讲到了跟请求相关的类了 关于AFNetworking 3.0 源码解读 的文章篇幅都会很长,因为不仅仅要把代码进行详细的的解释,还会大概讲解和代码相关的知识点. 上半篇: URI编码的知识 关于 ...
- AFNetworking 3.0 源码解读(四)之 AFURLResponseSerialization
本篇是AFNetworking 3.0 源码解读的第四篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...
- AFNetworking 3.0 源码解读(五)之 AFURLSessionManager
本篇是AFNetworking 3.0 源码解读的第五篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...
- AFNetworking 3.0 源码解读 总结(干货)(下)
承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...
随机推荐
- Android UI体验之全屏沉浸式透明状态栏效果
前言: Android 4.4之后谷歌提供了沉浸式全屏体验, 在沉浸式全屏模式下, 状态栏. 虚拟按键动态隐藏, 应用可以使用完整的屏幕空间, 按照 Google 的说法, 给用户一种 身临其境 的体 ...
- C#4.0泛型的协变,逆变深入剖析
C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...
- 游戏服务器菜鸟之C#初探一游戏服务
本人80后程序猿一枚,原来搞过C++/Java/C#,因为工作原因最后选择一直从事C#开发,因为读书时候对游戏一直比较感兴趣,机缘巧合公司做一个手游的项目,我就开始游戏服务器的折腾之旅. 游戏的构架是 ...
- android http 抓包
有时候想开发的时候想看APP发出的http请求和响应是什么,这就需要抓包了,这可以得到一些不为人知的api,比如还可以干些“坏事”... 需要工具: Fiddler2 抓包(点击下载) Android ...
- 用Kotlin创建第一个Android项目(KAD 01)
原文标题:Create your first Android project using Kotlin (KAD 01) 作者:Antonio Leiva 时间:Nov 21, 2016 原文链接:h ...
- 报错:You need to use a Theme.AppCompat theme (or descendant) with this activity.
学习 Activity 生命周期时希望通过 Dialog 主题测试 onPause() 和 onStop() 的区别,点击按钮跳转 Activity 时报错: E/AndroidRuntime: FA ...
- Spark的StandAlone模式原理和安装、Spark-on-YARN的理解
Spark是一个内存迭代式运算框架,通过RDD来描述数据从哪里来,数据用那个算子计算,计算完的数据保存到哪里,RDD之间的依赖关系.他只是一个运算框架,和storm一样只做运算,不做存储. Spark ...
- SCNU ACM 2016新生赛决赛 解题报告
新生初赛题目.解题思路.参考代码一览 A. 拒绝虐狗 Problem Description CZJ 去排队打饭的时候看到前面有几对情侣秀恩爱,作为单身狗的 CZJ 表示很难受. 现在给出一个字符串代 ...
- angular中使用ngResource模块构建RESTful架构
ngResource模块是angular专门为RESTful架构而设计的一个模块,它提供了'$resource'模块,$resource模块是基于$http的一个封装.下面来看看它的详细用法 1.引入 ...
- android内部培训视频_第四节(1)_异步网络操作
第四节(1):异步网络操作 一.结合asyncTask下载网络图片 1.定义下载类,继承自asyncTask,参数分别为:String(url地址),Integer(刻度,本例没有用到),BitMa ...