iOS应用安全之HTTP/HTTPS详解(AFNetworking配套策略)

目录

HTTP缺点

HTTPS底层原理(SSL握手详解

普通RSA身份验证的隐患

证书和SSL握手底层原理

iOS原生方式校验HTTPS

使用AFNetworking来支持HTTPS(组合策略源码分析)

HTTPS网站访问输入后整个OSI模型流转过程


 

前言

这是早期的理解传送门,现在理解更深入点,还是记录下来

HTTPS一般开启之后默认网络请求框架是会给我们做最基本的校验,因此很多人压根不知道AF到底做了什么?不知道HTTPS整个流程,不知道AF中HTTPS验证模块下的具体细节。因为AF默认的校验模式符合一般安全策略,可以不写任何代码,都直接把HTTP改成HTTPS即可。

1.如果你想知道HTTPS底层原理和证书原理

2.如果你想知道如何自己设置AFNetworking来适配HTTPS

3.如果你想知道如何根据需求,调整HTTPS安全策略

4.如果你想知道抓包工具基本原理,为什么HTTPS了有些能被抓包,有些确不行?

5.如果你想知道输入一个https网站的访问全过程

6.如果你闲事情不够大

那么请继续往下看,不然就根据目录选择看吧

  

下面就详细介绍下。

HTTP缺点

首先网络分层模型最好仔细了解下做了什么,比如Web上输入网页到请求到资源的整个过程是怎么样的?

网络各层传输实现协议

不了解的可以参考下,文章最后我也会再次全部概括下。

http就不介绍了,无非就是应用层的协议,请求行,请求头,请求体,响应头,响应体,状态码等http传输协议格式。

HTTP缺点

  • 通信使用明文,可能被窃听
  • 不验证通信方的身份,可能遭遇伪装
  • 无法证明报文的完整性,有可能遭遇篡改

 

 

HTTPS底层原理(SSL握手详解)

技术点介绍

HTTP+加密+认证+完整性保护 = HTTPS

HTTP协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险,而协议TLS/SSL具有身份验证、信息加密和完整性校验的功能,可以避免此类问题发生。

TLS/SSL全称安全传输层协议Transport Layer Security, 是介于TCP和HTTP之间的一层安全协议,不影响原有的TCP协议和HTTP协议,所以使用HTTPS基本上不需要对HTTP页面进行太多的改造。

HTTPS是在HTTP上建立SSL加密层,并对传输数据进行加密,是HTTP协议的安全版。HTTPS主要作用是:

  • 对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全
  • 对网站服务器进行真实身份认证

HTTPS协议的主要功能基本都依赖于TLS/SSL协议,TLS/SSL的功能实现主要依赖于三类基本算法:散列函数 Hash、对称加密和非对称加密,其利用非对称加密实现身份认证和密钥协商,对称加密算法采用协商的密钥对数据加密,基于散列函数验证信息的完整性。

先来看下用到的几个技术点

散列函数Hash

常见的有 MD5、SHA1、SHA256,该类函数特点是函数单向不可逆、对输入非常敏感、输出长度固定,针对数据的任何修改都会改变散列函数的结果,用于防止信息篡改并验证数据的完整性; 在信息传输过程中,散列函数不能单独实现信息防篡改,因为明文传输,中间人可以修改信息之后重新计算信息摘要,因此需要对传输的信息以及信息摘要进行加密;

对称加密

常见的有AES-CBC、DES、3DES、AES-GCM等,相同的密钥可以用于信息的加密和解密,掌握密钥才能获取信息,能够防止信息窃听,通信方式是1对1; 对称加密的优势是信息传输1对1,需要共享相同的密码,密码的安全是保证信息安全的基础,服务器和 N 个客户端通信,需要维持 N 个密码记录,且缺少修改密码的机制;

非对称加密

即常见的 RSA 算法,还包括 ECC、DH 等算法,算法特点是,密钥成对出现,一般称为公钥(公开)和私钥(保密),公钥加密的信息只能私钥解开,私钥加密的信息只能公钥解开。因此掌握公钥的不同客户端之间不能互相解密信息,只能和掌握私钥的服务器进行加密通信,服务器可以实现1对多的通信,客户端也可以用来验证掌握私钥的服务器身份。 非对称加密的特点是信息传输1对多,服务器只需要维持一个私钥就能够和多个客户端进行加密通信,但服务器发出的信息能够被所有的客户端解密,且该算法的计算复杂,加密速度慢。

结合三类算法的特点,TLS的基本工作方式是,客户端使用非对称加密与服务器进行通信,实现身份验证并协商对称加密使用的密钥, 然后对称加密算法采用协商密钥对信息以及信息摘要进行加密通信,不同的节点之间采用的对称密钥不同,从而可以保证信息只能通信双方获取。

普通RSA身份验证的隐患

身份验证和密钥协商是TLS的基础功能,要求的前提是合法的服务器掌握着对应的私钥。但RSA算法无法确保服务器身份的合法性,因为公钥并不包含服务器的信息,存在安全隐患:

  • 客户端C和服务器S进行通信,中间节点M截获了二者的通信;
  • 节点M自己计算产生一对公钥pub_M和私钥pri_M;
  • C向S请求公钥时,M把自己的公钥pub_M发给了C;
  • C使用公钥 pub_M加密的数据能够被M解密,因为M掌握对应的私钥pri_M,而 C无法根据公钥信息判断服务器的身份,从而 C和 * M之间建立了"可信"加密连接;
  • 中间节点 M和服务器S之间再建立合法的连接,因此 C和 S之间通信被M完全掌握,M可以进行信息的窃听、篡改等操作。
  • 另外,服务器也可以对自己的发出的信息进行否认,不承认相关信息是自己发出。

证书和SSL握手底层原理

证书制作:

1.服务方S向第三方机构CA提交公钥、组织信息、个人信息(域名)等信息并申请认证;

2.CA通过线上、线下等多种手段验证申请者提供信息的真实性,如组织是否存在、企业是否合法,是否拥有域名的所有权等;

3.如信息审核通过,CA会向申请者签发认证文件-证书。 证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构 CA的信息、有效时间、证书序列号等信息的明文,同时包含一个签名; 签名的产生算法:首先,使用散列函数计算公开的明文信息的信息摘要,然后,采用 CA的私钥对信息摘要进行加密,密文即签名;

注意点:

服务器自己有一套公钥私钥,传输是不安全的,就是上面提到的RSA隐患,因此介入了CA权威机构,申请的时候把公钥给CA进行验证,然后CA拿着公钥一起其他信息弄成证书。先把证书理解成一段数据,然后把这个数据进行HASH摘要算法,然后CA公司有自己的私钥,拿着这个私钥把摘要进行RAS加密,加密后的数据就是签名。因此证书就是明文公钥等其他信息,加上CA公司用他们自己私钥签名的一个文件。详细可以看上面的证书图

SSL握手

首先明白网络各层的协议和作用,可以参考文章头部的链接。SSL握手是发生在TCP三次握手之后的

第一阶段:ClientHello

客户端发起请求,以明文传输请求信息,包含版本信息,加密套件候选列表,压缩算法候选列表,随机数random_C,扩展字段等信息。

第二阶段:ServerHello-ServerHelloDone

如上图可以看出这个阶段包含4个过程( 有的服务器是单条发送,有的是合并一起发送)。服务端返回协商的信息结果,包括选择使用的协议版本,选择的加密套件,选择的压缩算法、随机数random_S等,其中随机数用于后续的密钥协商。服务器也会配置并返回对应的证书链Certificate,用于身份验证与密钥交换。然后会发送ServerHelloDone信息用于通知服务器信息发送结束。

这里1和2的随机数现在客户端和服务端都有了,后续会用到

第三阶段:证书校验

客户端这边还需要对服务器返回的证书进行校验。只有证书验证通过后,才能进行后续的通信。(具体分析可参看后续的iOS证书验证过程)

简单介绍下:

1.明白证书的组成,就是上面那段介绍证书制作的过程,服务器把证书传输给客户端的时候,我们是能看到证书公钥信息,过期时间,序列号等明文信息的,还有一个CA的签名(签名算法很重要,用来身份验证)。

2.如果没有这个签名,我们直接拿到明文公钥信息,鬼知道是不是真的?重点来了,签名是RAS了摘要算法,RSA的密钥就是CA的密钥,那么解密只能用CA的公钥解。这里千万区分开了,这里的公钥和我们服务器的公钥是不一样的,整个过程是有两对RSA密钥的。千万别搞错了。这就涉及到了电脑,手机等硬件里面的根证书列表的。正常情况下,生产的时候就会把权威机构的例如CA的公钥加入进去。然后Apple给我们提供了这个方法 SecTrustEvaluate 用来验证证书的。一样先用系统的根证书列表拿到对应的公钥进行解密服务器传过来证书里面的签名,这里解出来的是例如是MD5的摘要值,这个值是我们之前拿公钥去CA认证的时候摘要出来的。然后客户端也拿出证书的明文进行MD5,把这两个值一比对,如果相同,可以验证两个观点。

第一,认证服务器的公开密钥的是真实有效的数字证书认证机构。(因为公私药是成对的,能解开说明是CA机构的)

第二,服务器的公开密钥是值得信赖的。(MD5没变,说明是原装的,肯定是值得信赖的公钥,没被篡改)

先记得一点,这种验证,只能说明这个证书是正规机构颁发的而已,而且信息没被篡改,但是如何确定是我自己服务器的证书?后面有介绍

 

第四阶段:ClientKeyExchange-Finished

服务器返回的证书验证合法后, 客户端计算产生随机数字Pre-master Key(预设主密钥),并用server证书中公钥加密,发送给服务器。同时客户端会根据已有的三个随机数根据相应的生成协商密钥(主密钥+两个随机数通过复杂算法生成真正的传输密钥)。客户端会通知服务器后续的通信都采用协商的通信密钥和加密算法进行加密通信。然后客户端发送Finished消息用于通知客户端信息发送结束。

第五阶段:服务器端生成协商密钥

服务器也会根据已有的三个随机数使用相应的算法生成协商密钥,会通知客户端后续的通信都采用协商的通信密钥和加密算法进行加密通信。然后发送Finished消息用于通知服务器信息发送结束。

第六阶段:握手结束

在握手阶段结束后,客户端和服务器数据传输开始使用协商密钥进行加密通信。

 

HTTPS接入的耗时如何优化:

CDN接入

HTTPS 增加的延时主要是传输延时 RTT,RTT 的特点是节点越近延时越小,CDN 天然离用户最近,因此选择使用 CDN 作为 HTTPS 接入的入口,将能够极大减少接入延时。CDN 节点通过和业务服务器维持长连接、会话复用和链路质量优化等可控方法,极大减少 HTTPS 带来的延时。

会话缓存

虽然前文提到 HTTPS 即使采用会话缓存也要至少1*RTT的延时,但是至少延时已经减少为原来的一半,明显的延时优化;同时,基于会话缓存建立的 HTTPS 连接不需要服务器使用RSA私钥解密获取 Pre-master 信息,可以省去CPU 的消耗。如果业务访问连接集中,缓存命中率高,则HTTPS的接入能力讲明显提升。当前TRP平台的缓存命中率高峰时期大于30%,10k/s的接入资源实际可以承载13k/的接入,收效非常可观。

硬件加速

为接入服务器安装专用的SSL硬件加速卡,作用类似 GPU,释放 CPU,能够具有更高的 HTTPS 接入能力且不影响业务程序的。测试某硬件加速卡单卡可以提供35k的解密能力,相当于175核 CPU,至少相当于7台24核的服务器,考虑到接入服务器其它程序的开销,一张硬件卡可以实现接近10台服务器的接入能力。

远程解密

本地接入消耗过多的 CPU 资源,浪费了网卡和硬盘等资源,考虑将最消耗 CPU 资源的RSA解密计算任务转移到其它服务器,如此则可以充分发挥服务器的接入能力,充分利用带宽与网卡资源。远程解密服务器可以选择 CPU 负载较低的机器充当,实现机器资源复用,也可以是专门优化的高计算性能的服务器。当前也是 CDN 用于大规模HTTPS接入的解决方案之一。

 


以上就是HTTPS证书原理和SSL层协议的原理,解决了文章头部提出的三个HTTP的问题

  • 通信使用明文,可能被窃听(协商加密解决)
  • 不验证通信方的身份,可能遭遇伪装 (客户端验证证书机制和原理)
  • 无法证明报文的完整性,有可能遭遇篡改 (验证HASH摘要是否一致)

简单的来说,SSL/TSL通过四次握手,主要交换三个信息:

  1. 数字证书:该证书包含了公钥等信息,一般是由服务器发给客户端,接收方通过验证这个证书是不是由信赖的CA签发,或者与本地的证书相对比,来判断证书是否可信;假如需要双向验证,则服务器和客户端都需要发送数字证书给对方验证;
  2. 三个随机数:这三个随机数构成了后续通信过程中用来对数据进行对称加密解密的“对话密钥”

    首先客户端先发第一个随机数N1,然后服务器回了第二个随机数N2(这个过程同时把之前提到的证书发给客户端),这两个随机数都是明文的;而第三个随机数N3(这个随机数被称为Premaster secret),客户端用数字证书的公钥进行非对称加密,发给服务器;而服务器用只有自己知道的私钥来解密,获取第三个随机数。这样,服务端和客户端都有了三个随机数N1+N2+N3,然后两端就使用这三个随机数来生成“对话密钥”,在此之后的通信都是使用这个“对话密钥”来进行对称加密解密。因为这个过程中,服务端的私钥只用来解密第三个随机数,从来没有在网络中传输过,这样的话,只要私钥没有被泄露,那么数据就是安全的。

  3. 加密通信协议:就是双方商量使用哪一种加密方式,假如两者支持的加密方式不匹配,则无法进行通信;

有个常见的问题,关于随机数为什么要三个?只最后一个随机数N3不可以么?

这是由于SSL/TLS设计,就假设服务器不相信所有的客户端都能够提供完全随机数,假如某个客户端提供的随机数不随机的话,就大大增加了“对话密钥”被破解的风险,所以由三组随机数组成最后的随机数,保证了随机数的随机性,以此来保证每次生成的“对话密钥”安全性。

上面的只是原理,好奇的可以看看,不喜欢看的可以直接看下面iOS端如何来选择各种安全策略


iOS原生方式校验HTTPS

这是之前写的,有点乱,但是有些图逻辑还是很好

1). 第一步,先获取需要验证的信任对象(Trust Object)。这个Trust Object在不同的应用场景下获取的方式都不一样

对于NSURLConnection来说,是从delegate方法
-connection:willSendRequestForAuthenticationChallenge:
回调回来的参数challenge中获取
([challenge.protectionSpace serverTrust])。

2). 使用系统默认验证方式验证Trust Object。

SecTrustEvaluate

会根据Trust Object的验证策略,一级一级往上,验证证书链上每一级数字签名的有效性(这一部分可以百度下证书链),从而评估证书的有效性。

3). 如第二步验证通过了,一般的安全要求下,就可以直接验证通过,进入到下一步:

使用Trust Object生成一份凭证
([NSURLCredential credentialForTrust:serverTrust])
传入challenge的sender中
([challenge.sender useCredential:cred forAuthenticationChallenge:challenge])
处理,建立连接。

4). 假如有更强的安全要求,可以继续对Trust Object进行更严格的验证。常用的方式是在本地导入证书,验证Trust Object与导入的证书是否匹配。下面会有AF的介绍

5). 假如验证失败,取消此次验证流程,拒绝连接请求。

ps: 假如是自建证书的,则不使用第二步系统默认的验证方式,因为自建证书的根CA的数字签名未在操作系统的信任列表中。

iOS授权验证的API和流程大概了解了,下面,我们看看在NSURLConnection中的代码实现:

// Now start the connection
NSURL * httpsURL = [NSURL URLWithString:@"https://www.google.com"];
self.connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:httpsURL] delegate:self];

    
//回调
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
 	//1)获取trust object
	SecTrustRef trust = challenge.protectionSpace.serverTrust;
	SecTrustResultType result;
	
	//2)SecTrustEvaluate对trust进行验证
	OSStatus status = SecTrustEvaluate(trust, &result);
	if (status == errSecSuccess &&
		(result == kSecTrustResultProceed ||
       	result == kSecTrustResultUnspecified)) {
       	
       	//3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接
		NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
		[challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
		
    } else {
    
    	//5)验证失败,取消这次验证流程
    	[challenge.sender cancelAuthenticationChallenge:challenge];
    	
  }
}

上面是代码是通过系统默认验证流程来验证证书的。假如我们是自建证书的呢?这样Trust Object里面服务器的证书因为不是可信任的CA签发的,所以直接使用SecTrustEvaluate进行验证是不会成功。又或者,即使服务器返回的证书是信任CA签发的,又如何确定这证书就是我们想要的特定证书?这就需要先在本地导入证书,设置成需要参与验证的Anchor Certificate(锚点证书,通过SecTrustSetAnchorCertificates设置了参与校验锚点证书之后,假如验证的数字证书是这个锚点证书的子节点,即验证的数字证书是由锚点证书对应CA或子CA签发的,或是该证书本身,则信任该证书,还有个Only方法),再调用SecTrustEvaluate来验证。代码如下:

//先导入证书
NSString * cerPath = ...; //证书的路径
NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
self.trustedCertificates = @[CFBridgingRelease(certificate)];

//回调
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
 	//1)获取trust object
	SecTrustRef trust = challenge.protectionSpace.serverTrust;
	SecTrustResultType result;

	//注意:这里将之前导入的证书设置成下面验证的Trust Object的anchor certificate
  	SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)self.trustedCertificates);

	//2)SecTrustEvaluate会查找前面SecTrustSetAnchorCertificates设置的证书对trust进行验证
	OSStatus status = SecTrustEvaluate(trust, &result);
	if (status == errSecSuccess &&
		(result == kSecTrustResultProceed ||
       	result == kSecTrustResultUnspecified)) {
       	
       	//3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接
		NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
		[challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
		
    } else {
    
    	//5)验证失败,取消这次验证流程
    	[challenge.sender cancelAuthenticationChallenge:challenge];
    	
  }
}

注意:SecTrustSetAnchorCertificates

Apple官方介绍

博主介绍

设置锚点证书方法->SecTrustSetAnchorCertificates

设置锚点证书策略->SecTrustSetAnchorCertificatesOnly

这里有个小小的注意点,当我们把本地的证书调用SecTrustSetAnchorCertificates设置为锚点的时候,如果仅仅写了这句话,那么就要注意了,当我们再调用SecTrustEvaluate的时候,是只会匹配锚点证书是否和要匹配的服务器证书是否一致。仅仅匹配设置到的锚点证书,不会和之前一样SecTrustEvaluate会匹配系统根证书列表去验证。如果你两者都要验证,就要调用另一个方法,把参数设置为NO

OSStatus  status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
    SecTrustSetAnchorCertificatesOnly(serverTrust, NO);

什么意思呢?通俗点理解是,默认情况服务器证书是会拿去匹配系统根信任列表里面的证书,就是上面介绍的证书认证过程。如果加了锚点不调用SecTrustSetAnchorCertificatesOnly,也就是锚点证书替换系统信任列表的证书数组,那么如果两者都可以再数组中匹配服务器证书,就要SecTrustSetAnchorCertificatesOnly标记为NO。


SecTrustEvaluate(服务器证书)
A---> [[根证书列表]] -->系统根证书列表-->匹配就信任


SecTrustSetAnchorCertificates 设置锚点
B---->[[锚点证书列表(替换了之前的数组)]] --->锚点证书列表--->匹配就信任


SecTrustSetAnchorCertificates 设置锚点
SecTrustSetAnchorCertificatesOnly标记为NO
C---->[[系统根证书列表],[锚点证书列表]] --->锚点证书列表+系统根列表--->匹配一个就信任

 

使用AFNetworking来支持HTTPS(组合策略源码分析)

当你启用HTTPS的时候AF会启用AFSecurityPolicy这个类,关键参数如下

HTTPS校验模式,有三个
1.默认系统根证书校验
2.默认系统根证书 + 默认本地证书公钥匹配
3.默认系统根证书 + 默认本地证书完全匹配
@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;

本地证书.cer数组
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;

是否允许自签证书(也就是无效的证书),默认是NO
允许自签就要设置为YES,一般都不会开启
@property (nonatomic, assign) BOOL allowInvalidCertificates;

是否需要强域名校验 请求的域名和服务器证书域名是否一致的校验
假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;我们公司注册是通配符,因此YES无误
@property (nonatomic, assign) BOOL validatesDomainName;

//validatesCertificateChain 是否验证整个证书链,默认为YES
//设置为YES,会将服务器返回的Trust Object上的证书链与本地导入的证书进行对比,这就意味着,假如你的证书链是这样的:
//GeoTrust Global CA 
//    Google Internet Authority G2
//        *.google.com
//那么,除了导入*.google.com之外,还需要导入证书链上所有的CA证书(GeoTrust Global CA, Google Internet Authority G2);
//如是自建证书的时候,可以设置为YES,增强安全性;假如是信任的CA所签发的证书,则建议关闭该验证,因为整个证书链一一比对是完全没有必要(请查看源代码);
securityPolicy.validatesCertificateChain = NO;

首先AF底层是URLSession,文章上半部分介绍的底层SSL握手原理,下面这个方法就是URLSession代理方法回调的证书验证方法

- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
//挑战处理类型为 默认
    /*
     NSURLSessionAuthChallengePerformDefaultHandling:默认方式处理
     NSURLSessionAuthChallengeUseCredential:使用指定的证书
     NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消挑战
     */
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    // 服务器挑战的证书
    __block NSURLCredential *credential = nil;
// 这个Block是提供给用户自定义证书挑战方式的,比如是否需要自定义
    if (self.sessionDidReceiveAuthenticationChallenge) {
        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
    } else {
    // NSURLAuthenticationMethodServerTrust 单向认证关系 也就是说服务器端需要客户端返回一个根据认证挑战的保护空间提供的信任产生的挑战证书
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        // 基于客户端的安全策略来决定是否信任该服务器,不信任的话,也就没必要响应挑战
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
             // 创建挑战证书(注:挑战方式为UseCredential和PerformDefaultHandling都需要新建挑战证书)
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                if (credential) {
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }
// 完成挑战
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

核心方法evaluateServerTrust,我用我自己的理解和验证加上了注释,都验证过了

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
    // 如果需要强域名校验的时候,而且允许自建证书,pinningMode不能为Non,必须为后两个,不然就认证失败
    // allowInvalidCertificates YES   validatesDomainName  YES   直接返回NO回调Cancel
    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
        // 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;
    }
    // 根据强域名校验添加对应的政策
    // 注意点1: AFServerTrustIsValid AF封装的系统SecTrustEvaluate根证书校验 校验CA颁发的或者我们手动信任的证书
    // 注意点2: validatesDomainName = YES强域名校验,增加了策略,再调用AFServerTrustIsValid的时候会 先校验CA颁发的或者我们手动信任的证书 然后再校验domain和服务器返回的证书域名是否匹配(就会and校验两者一起)这里的domai是  challenge.protectionSpace.host的值,官方的意思可以理解为请求URL的实际域名
    // Get the proxy host if this is a proxy authentication, or the host from the URL.
    
    NSMutableArray *policies = [NSMutableArray array];
    if (self.validatesDomainName) {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
    }
    // 为serverTrust设置验证策略,即告诉客户端如何验证serverTrust(增加域名策略)
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
    // 核心判断 如果是PinningModeNone,如果允许自建证书,直接反悔YES,如果不允许自建证书,就调用系统跟证书认证方式去验证服务器证书
    if (self.SSLPinningMode == AFSSLPinningModeNone) {
        return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
        // 前提是不允许自建证书,而且服务器证书也是自建证书,自然无法通过系统根证书认证,一般自建证书无法通过这个判断,就返回NO
        // 如果是PublicKey和AFSSLPinningModeCertificate两个类型,那么肯定会先调用AFServerTrustIsValid让系统的根证书校验返回是YES,在进行后续的校验例如公钥和证书完全匹配
    } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
        return NO;
    }
    // 强行过滤 避免上面的if出错又进来这里,直接返回NO
    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)];
            }
            // 设置为锚点 如果没有调用 SecTrustSetAnchorCertificatesOnly 标记NO
            // 那么设置完锚点之后,再调用AFServerTrustIsValid,匹配的不再是系统根证书,而是你自己导入的pinnedCertificates仅此而已
            // SecTrustSetAnchorCertificatesOnly 标记为NO 代表可以匹配系统根证书列表,也可以匹配锚点,两者是or的关系,,满足之一就好
            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 = 0;
            NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
            
            for (id trustChainPublicKey in publicKeys) {
                for (id pinnedPublicKey in self.pinnedPublicKeys) {
                    if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                        trustedPublicKeyCount += 1;
                    }
                }
            }
            return trustedPublicKeyCount > 0;
        }
    }
    
    return NO;
}

OK,AF就是简单的这几个参数,但是很多人不知道什么意思。底层的校验逻辑就是上面的一坨核心代码,我们看看下面的组合逻辑 组合了9个,各种策略可以参考下

AFSecurityPolicy  *securityPolicy_f = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];//AFSSLPinningModeNone
    securityPolicy_f.validatesDomainName = YES;

    securityPolicy_f.allowInvalidCertificates = NO;

    manager.securityPolicy = securityPolicy_f;

 

场景

Mode

ACVD验证策略适用场景不适合此策略
1NoneNOYES

1.验证证书是否为信任的颁发机构颁发或是否为用户手动信任的证书

2.验证证书是否过期

3.验证证书域名是否匹配

1.AF默认的安全策略

2.对于安全有基础的要求

3.使用CA机构颁发的证书

4.charles自建证书可以手动信任,但是强域名校验开启,就不允许抓包,返回-999

1.使用自制证书的

2.允许抓包

 

2NoneNONO

1.验证证书是否为信任的颁发机构颁发或是否为用户手动信任的证书

2.验证证书是否过期

1.证书是正规CA颁发的。但是使用的域名不是证书中的域名。域名可以随意

2.charles自建证书可以抓包,因为没有强域名校验

1.存在风险,会导致攻击方使用自己的合法的CA证书进行攻击

2.使用自制证书的

3.不可以抓包

3NoneYESYES /NO

1.VD=YES AF直接返回NO,拒绝掉,可以看源码

2、VD=NO不对证书做任何验证

请勿使用这儿配置。 1.对安全没有要求的1.对安全有要求的
4PublicKeyNOYES

1.验证证书是否为信任的颁发机构颁发或是否为用户手动信任的证书

2.验证证书是否过期

3.验证证书域名是否匹配

4.验证证书和埋入的证书的公钥是否一致

1.证书是正规CA颁发的。

2.对安全有比较高的需求

3.需要本地APP中导入证书

4.禁止第三方工具抓包(这是是根证书校验返回NO)

5.证书过期后只要保证公钥一致,就可以保证请求有效

6.请求域名和证书域名要一致

1.使用自制证书的

2.害怕攻击者拿到私钥或公钥文件,伪造证书(概率极低,因为需要CA机构再签发)

3.证书过期需要更换,但是新旧证书公钥不同

5PublicKeyNONO

1.验证证书是否为信任的颁发机构颁发或是否为用户手动信任的证书

2.验证证书是否过期

3.验证证书和埋入的证书的公钥是否一致

1.证书是正规CA颁发的。

2.需要本地APP中导入证书

3.禁止第三方工具抓包(这里是根证书校验成功,本地证书公钥校验返回NO)

4.使用的域名和证书域名不一致

5.证书过期后只要保证公钥一致,就可以保证请求有效

1.使用自制证书的2.害怕攻击者拿到私钥或公钥文件,伪造证书(概率极低,因为需要CA机构再签发)

3.证书过期需要更换,但是新旧证书公钥不同

6PublicKeyYESYES/NO

1.验证证书和埋入的证书公钥是否一致

2.只要开启AC的YES,就不会校验根证书的逻辑,这里就会匹配本地证书的公钥

1.使用自制证书

2.需要本地APP中导入证书

3.禁止第三方工具抓包(这里不需要再校验根证书成功与否,AC是YES了,但是本地公钥校验失败返回NO,无法抓包)

4.不需要关心证书的有效期

1.攻击者可以拿到私钥或公钥文件,伪造证书。相对于场景4和5,更容易攻击一些。

2.攻击者可以用不在有效期的证书对进行攻击

7CertificateNOYES

1.验证证书域名是否匹配

2.验证证书是否为信任的颁发机构颁发或是否为用户手动信任的证书

3.验证证书是否过期

4.验证证书和埋入的证书是否完全一致

1.证书是正规CA颁发的。

2.对安全有最高的需求

3.需要本地APP中导入证书

4.禁止第三方工具抓包(和Publickey一样,当需要强域名校验的时候,根证书匹配方法既会匹配根证书,也会校验域名,因此抓包就肯定校验不过返回NO)

1.需要考虑证书更新的场景

2.证书如果失效,客户端网络请求将会失效

3.自制证书

8CertificateNONO

1.验证证书是否为信任的颁发机构颁发或是否为用户手动信任的证书

2.验证证书是否过期

3.验证证书和埋入的证书是否完全一致

1.证书是正规CA颁发的。

2.对安全有最高的需求

3.需要本地APP中导入证书

4.禁止第三方工具抓包(第一个系统根证书验证方法是YES,后面进入switch分支,设置了锚点证书,但是SecTrustSetAnchorCertificatesOnly方法没有调用,因此,后续的根证书匹配都只会匹配锚点证书,替换了,因此抓包返回的证书及时加入到系统信任列表,一样匹配不上,返回NO)

5.证书域名和实际域名不一致

1.需要考虑证书更新的场景

2.证书如果失效,客户端网络请求将会失效

3.自制证书

4.攻击者拿到公私钥的前提下,可以利用不校验域名,攻击或重定向其他域名。

9CertificateYESYES/NO

1.验证证书和埋入的证书整个信息是否完全一致

1.使用自制证书

2.需要本地APP中导入证书

3.禁止中间人攻击

4.这里的证书完全匹配就是强域名校验了

1.需要考虑证书更新的场景

2.证书如果失效,客户端网络请求将会失效

3.无法作废不安全的证书。在攻击者拿到公私钥的前提下,可以监听数据。

这几个策略我都验证过了,后面会介绍如何选择和使用,以及各自的区别。看下AF的代码是如何进行校验的,其实就是设置几个属性字段如下

首先明白一点,validateDomainName校验是根据请求的URL域名和服务器返回的证书域名是否匹配校验用的

策略1

AFSecurityPolicy  *securityPolicy_f = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];//AFSSLPinningModeNone
    securityPolicy_f.validatesDomainName = YES;
    securityPolicy_f.allowInvalidCertificates = NO;
    manager.securityPolicy = securityPolicy_f;

该策略下调用系统AFServerTrustIsValid验证根证书的时候,由于加入了强域名校验,本来没加域名校验的时候即使是HTTPS,用了Charles的根证书,直接信任装到iphone手机里面,也能验证通过,这种属于系统级别的用户自己安装信任,因此不要随便信任其他根证书,会有安全隐患。但是如果你加了域名校验,如果通信正常没有被劫持,肯定能通过,但是如果被中间人攻击,返回的证书和domain就不能匹配了,因此无法被抓包。

策略8

AFSecurityPolicy  *securityPolicy_f = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];//AFSSLPinningModeNone
    securityPolicy_f.validatesDomainName = NO;
    securityPolicy_f.allowInvalidCertificates = NO;
    manager.securityPolicy = securityPolicy_f;

该策略下,没有加入域名校验,校验系统根证书的时候能通过,就会进入后续Cettificate分支里面匹配证书。分支里面会把AF搜集到的.cer证书文件加入数组,然后设置为锚点证书,这里只有设置,没有清除标记(没调用CertificatesOnly),所以只会校验锚点证书是否和服务器证书能完全匹配。如果被中间人攻击,第一步根证书校验能通过,比如Charles你主动把证书加入到iPhone并且信任,因此会进入第二步校验,证书匹配,如果被攻击,肯定匹配不上,网络错误返回cancel -999,就能低抵御攻击。

FIddler和Charles如何进行iPhone的HTTPS抓包的(中间人攻击原理)

第一步, charles向服务器发送请求进行握手, 获取到服务器的CA证书, 用根证书公钥进行解密, 验证服务器数据签名,获取到服务器CA证书公钥。这一步其实charles模拟的就是普通用户访问服务端,为了拿到通信的公钥而已。
第二步, charles伪造自己的CA证书, 冒充服务器证书传递给客户端浏览器,客户端浏览器做跟charles一样的事。
第三步, 客户端浏览器生成https通信用的对称密钥,用charles伪造的证书公钥加密后传递给服务器, 被charles截获。
第四步, charles将截获的密文用自己伪造证书的私钥解开,获得https通信用的对称密钥。
第五步, charles将对称密钥用服务器证书公钥加密传递给服务器, 服务器用私钥解开后建立信任,握手完成, 用对称密钥加密消息, 开始通信。
第六步, charles接收到服务器发送的密文, 用对称密钥解开,获得服务器发送的明文。再次加密, 发送给客户端浏览器。
第七步, 客户端向服务器发送消息, 用对称密钥加密, 被fidller截获后,解密获得明文。
由于charles一直拥有通信用对称密钥, 所以在整个https通信过程中信息对其透明。也就是说fiddler&charles向服务器冒充是客户端,向客户端又谎称自己是服务器。

差不多就是两套RSA密钥,首先charles当做客户端向目标服务器请求,ssl握手获取证书保存公钥。然后抓包的时候我们会设置代理而且信任根证书装到手机上抓包HTTPS。我们的请求被charles截获,因此本来你和服务器建立的ssl,结果变成了你和charles建立了ssl,所有数据都发送给了charles,但是为什么能信任呢?不是根证书验证么?这可不,你自己不是刚把charles的证书装到手机并且信任了么,自然能验证合法性了,即便charles的证书是自制的。这时,ssl的时候传输的数据加密公钥,其实是charles的,你把数据传给charles,就能被charles解密(因为之前被截获,你其实就是和中间人在协商建立SSL连接了),然后charles模拟客户端请求,用原先得到的公钥加密去得到正确的密钥(charles和服务器建立SSL),来获取真实数据,然后所有的数据都会经过Charles,如果数据没有加密,即便是HTTPS,能被抓包数据或者攻击,就能看到明文了,这就是中间人攻击的原理。客户端和charles模拟一套,charles和服务器模拟一套。

 

 

如何选择上述方案

可以看到中间人攻击就是中间人想方设法让你信任他的证书到根证书列表,如果信任了,基本的HTTPS验证就能通过,数据就可以被抓包了。如果开启强域名校验,中间人跟着伪造了域名,你也一样会信任。如何根据自身环境选择方案?

1.即使信任根证书,也能被信任的方式攻击,如果加了强域名,中间人跟着证书域名一样会被攻击,那么只能把证书埋进去,做AFSSLPinningModePublicKey和AFSSLPinningModeCertificate这两个模式的验证,就是AF后续的几个if逻辑

2.双向验证模式(参考链接

3.给我们自己传输的请求体或者服务端给的json数据再来一套RSA和AES加密,即使被攻击,别人也只能看到一串乱码

  1. 如果不是金融类的,对安全要求没那么高,可以采用默认的,默认情况下就已经强域名校验,不能自建证书了,我验证了以下也不能抓包,网络错误返回-999
  2. 要校验域名,即:validatesDomainName不要设置为NO。如果设为NO,不校验域名,也最好自己加一层验证方法。
  3. 如果是自制证书,allowInvalidCertificates设置为YES。如果是ca颁发的证书则建议设置为NO。
  4. 无论是使用AFSSLPinningModePublicKey还是AFSSLPinningModeCertificate都应该考虑证书失效需要更换的问题。
  5. 如果用AFSSLPinningModePublicKey方式,使用场景4和5只要保证后续更换的证书公钥不变化就可以了。个人觉得是安全和方便性最平衡的一种模式,只要私钥不泄露就可以了。这个要求公司的证书管理机构知道这点,不过如果出了意外,也可以延缓部署。
  6. 最安全的方案是7。也就是强校验,漏洞最少,安全防护最高。但是必须考虑证书失效更换的问题。

安全级别弱一点就选择策略1,不过也能满足大部分需求了

安全级别强一点就用策略4,问题是证书过期如何更换

安全级别更强一点就用策略7,一样有证书过期问题

 

如何处理证书过期?

首先过期了,App所有的网络断开了,验证失败了。按我们现在的做法,就是把证书先埋进去,在服务端需要更换HTTPS证书的时候,提前几个月准备发版,然后把新证书也埋进去,新版本就强制用户升级,来慢慢过渡老用户,一般提示用户安全性能提高,请升级到最新版本,用户还是愿意的。策略4,搞起来,妥妥的。

不过权威机构也有告知我们说,最好别埋,有可能证书以后很容易过期,不好更新,我们买的通配符的,时间应该就一点,问题就还好。按他的建议,翻译过来就是采用策略1,系统根证书校验,然后开启强域名校验。最好的话我们自己混淆App,然后数据再次用RSA和AES那套加密一下,再去用HTTPS传输。这样安全性低了点,但是应用层自己身的加密逻辑能保证被攻击截取,中间人看到的数据也是加密后的。

还有几个方案

  1. 可以用上述场景PublicKey,保证后续更换的证书公钥不变化就可以了
  2. APP强制升级,全局通知,热更新等保护通道,建议不要使用强校验策略,使用强的加密手段保证安全,作为最后手段。
  3. 加入证书更新的通道,每次应用启动的时候访问,查看是否有证书更新,如果有就去下载证书。

 

App内部WebView等其他第三方请求如何验证HTTPS 

例如聊天SDK,SDWebImage,我看了源码和思路貌似调用的系统默认的方式,没有其他校验逻辑,这个没测试过,只是看别人代码是这么写的,猜测应该是启用HTTPS的服务的服务器都可以认证通过

- performDefaultHandlingForAuthenticationChallenge:

 

HTTPS网站访问输入后整个OSI模型流转过程

另一个博客详细介绍

1.首先了解下各种名词的意思

Mac地址:以太网卡物理上标记一台电脑

IP地址:逻辑上标记一台电脑

子网掩码:通过和IP地址与计算得出当前网段

默认网关:当一个网段需要访问另一个网段的时候,默认把数据先发送到路由器网关进行后续传递(官方介绍百度一堆)

DHCP服务器:插上网线的时候,寻找没有分配IP地址的电脑自动分配一个IP

DNS服务器:域名查找对应的IP(电话簿)

2.首先记录下各种设备相关的配置

PC:自带Mac地址,我们需要配置IP地址,还需要配置gateway网关地址,也就是两网卡一侧IP地址,发送的数据包的目的ip不是当前网络时,此数据包包转发的目的ip,还有个就是设置DNS服务器地址,当域名访问的时候,默认会先去DNS服务器解析

交换机:链接多台电脑,没啥好配置的,比集线器高级一点,另一端链接路由器网关

路由器:路由表设置,指定数据包下一跳的地址,static里面设置,路由器需要几个IP就给几个IP用

 

3.基本协议相关

arp:根据IP获取硬件Mac地址(网卡号)的协议

rarp:根据Mac地址找IP

icmp:ping电脑的协议

tcp:传输控制协议(有连接可靠)

udp:用户数据包协议(无连接,速度快,不可靠)

http:应用层协议传输数据

OSI四层模型
应用层
传输层
网络层(同一网段例如 192.168.1.255 和 本机IP都能接收到信号)  ip arp icmp
链路层 (链路层也一样,FFFF FFFF FFFF 全为F的Mac地址和本机网卡的Mac地址都能接收到)

 

OK。了解了上面的基本知识点,这些解释是我自己方便理解,官方介绍可以百度细细看,没必要说那么官方

1.访问https://www.bilibili.com(本机IP是私有192开头的C类的话会通过NAT协议再路由器进行公网本机IP转换)

2.PC上有配置了DNS(UDP方式)服务器,输入域名,一般都会去查找出对应的IP地址

3.而且配置了网关,这种不是本地网段访问,需要通过路由网关传输,因此第一次的时候,我们不知道路由网关的Mac地址,就会在TCP协议之前用ARP协议,发送到交换机上,然后交换机广播到网关,网关(交换机或者路由器或者目标主机)接收到包之后传回自己的Mac地址返回

4.组织数据,发送DNS(UDP)协议,带上域名发到网关(这里的IP还是DNS服务器IP,只是Mac地址是网关的Mac地址)

5.路由器根据自己的路由协议,选择一个较快的路径转发到目的网关

6.DNS所在服务器网关,发送包到DNS服务器,然后DNS服务器把域名对应的IP包返回(这里一样需要ARP获取返回的网关Mac)

7.客户端得到了www.bilibili.com的IP地址,然后发送TCP协议进行三次握手(TCP有序列号,校验和,ACK以及延时ACK,超时重发,连接管理,窗口控制,流量控制以及拥塞慢启动来保证是稳定的,连续的),三次握手的时候会根据链路层的MTU来确定数据是否需要分片,或者传输过程中通过ICMP协议来反馈处理异常信息

8.咱们这里是HTTPS,因此TCP三次握手之后就进入SSL层,开启校验逻辑,详细介绍看上面,简单说逻辑,首先服务器生成公钥私钥,拿公钥去CA机构去认证,证书由铭文+Sign组成,然后SSL层先传输加密支持表单,然后服务端返回证书客户端验证,这里的验证就是本文介绍的验证策略。通过之后,客户端拿着证书的公钥,把主密钥RSA后传给服务器,然后发送报文,两端拿着各自传输的时候拿到的三个随机数进行算法计算出AES的对称密钥,这个密钥是没有进行传输的,直接计算出来的,通过一段报文验证密钥是否一致,然后所有的http数据被SSL拿到,通过这个密钥和算法加密传输

8.链接成功之后会使用HTTP数据,在SSL协议层加密发送给服务器,请求服务器的数据,后续数据都是加密的

9.服务器接收到请求会后查询服务器,把对应的结果返回到浏览器,这里按Python DJango搭建的服务器为例,如果不是Nginx配置的静态资源,会进入DJango编写的URL路径匹配里面,然后根据业务返回对应的模板HTML或者是JSON数据

10.浏览器接收到数据,渲染显示网页

11.浏览器关闭。TCP四次挥手,结束,这里为什么不是三次挥手?由于TCP采用窗口模式来保证传输速度,这样就会有各自的缓冲区,例如客户端发送FIN包,服务端接收到放入缓冲区,这个时候不可能直接携带FIN包回去,肯定是要等待缓冲区数据处理完之后才进行FIN,这就是为什么需要四次,而不是三次。

 

补充

RSA或者SM2这种非对称加密看到一段文字,记录下

其实公钥和私钥都可以用来加密或解密---只要能保证用A加密,就用B解密就行。至于A是公钥还是私钥,其实可以根据不同的用途而定。

例如说,如果你想把某个消息秘密的发给某人,那你就可以用他的公钥加密。因为只有他知道他的私钥,所以这消息也就只有他本人能解开,于是你就达到了你的目的。

但是如果你想发布一个公告,需要一个手段来证明这确实是你本人发的,而不是其他人冒名顶替的。那你可以在你的公告开头或者结尾附上一段用你的私钥加密的内容(例如说就是你公告正文的一段话),那所有其他人都可以用你的公钥来解密,看看解出来的内容是不是相符的。如果是的话,那就说明这公告确实是你发的---因为只有你的公钥才能解开你的私钥加密的内容,而其他人是拿不到你的私钥的。

最后再说一下数字签名。
数字签名无非就两个目的:
证明这消息是你发的
证明这消息内容确实是完整的---也就是没有经过任何形式的篡改(包括替换、缺少、新增)。

其实,上面关于“公告”那段内容,已经证明了第一点:证明这消息是你发的。
那么要做到第二点,也很简单,就是把你公告的原文做一次哈希(md5或者sha1都行),然后用你的私钥加密这段哈希作为签名,并一起公布出去。当别人收到你的公告时,他可以用你的公钥解密你的签名,如果解密成功,并且解密出来的哈希值确实和你的公告原文一致,那么他就证明了两点:这消息确实是你发的,而且内容是完整的。

 

iOS认证挑战API

HTTPS中间人攻击和证书校验

单向双向认证

charles模拟中间人原理

iOS适配HTTPS

iOS HTTPS AF详细策略分析

AFNetworking分析源码

早期博客链接

图解HTTPS

架设CDN

HTTPS和HTTP详解

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值