ATS详解

恰逢最近面试的时候有被问到这一块,工作中其实也会经常遇到这方面的问题,但是原理并没有明明白白地搞下来,现在就来详细了解一番吧。

来,先从基本的原理入手,首先介绍一下一些安全协议。

  • TLS(Transport Layer Security,传输层安全协议)是SSL的标准化版本,有1.0、1.1、1.2、1.3四个版本。是一种对基于网络的传输的加密协议,可以在受信任的第三方公证基础上做双方的身份认证。TLS可以用在TCP上,也可以用在无连接的UDP报文上。协议规定了身份认证、算法协商、密钥交换等的实现。而TLS 1.0和SSL 3.0几乎没有区别,就相当于SSL 3.1;
  • SSL(Secure Socket Layer,安全套接字层)由网景Netscape公司开发的,有1、2、3三个版本,是TLS的前身,现在已不再更新;
  • HTTPS是在基于TLS/SSL的安全套接字上的的应用层协议,除了传输层进行了加密外,其它与常规HTTP协议基本保持一致;
  • 证书是TLS协议中用来对身份进行验证的机制,是一种数字签名形式的文件,包含证书拥有者的公钥及第三方的证书信息。

SSL工作原理

SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层: SSL记录协议(SSL Record Protocol),它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。 SSL握手协议(SSL Handshake Protocol),它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。

身份认证

现假设Alice要认证Bob的身份:

A–>B hello,areyou bob?
B–>A Alice,This Is bob{digest[Alice,This Is Bob]}bobs-private-key

Bod使用RSA的私钥加密一个消息摘要,例如对消息进行MD5 hash后加密,然后连同消息原文发给Alice。
Alice使用RSA的公钥解密,将解密的信息与消息原文的摘要对比。此为数字签名

为了解决中间人攻击,标准化组织在分发公钥这步协议上发明了证书。证书是由证书颁发者(即其上一级证书)使用其私钥签名而成,外界都可以通过证书知道颁发者的公钥。

这样就有了更安全的签名:

A–>B hello
B–>A Hi, I’m Bob, bobs-certificate
(A:verify bobs-certificate)
A–>B prove it
B–>A Alice, This Is bob{ digest[Alice, This Is Bob] }bobs-private-key

交换秘钥

有了证书的数字签名认证后,就可以进一步优化密文的交换方式。下面是先用不对称的加密算法法验证身份,然后用对称加密算法互通其他信息

A–>B hello
B–>A Hi, I’m Bob, bobs-certificate
A–>B prove it
B–>A Alice, This Is bob{ digest[Alice, This Is Bob] }bobs-private-key
A–>B ok bob, here is a secret {secret-key} bobs-public-key
B–>A {some message}secret-key

但是上面这种先认证身份后再对称加密传输数据的方式还是有漏洞,中间人虽然无法破解加密信息,却可以在得到身份认证后破坏对称加密信息来进行暴力碰撞。所以再有一种协议来解决,就是加入消息验证码(MAC)。

消息验证

消息验证码(MAC)根据对称秘钥和消息原文计算生成一个验证码,作为消息的一部分一并进行对称加密后传递,接收方解密后则将消息原文和对称秘钥再进行一次MAC制作,然后和接收到的MAC对比,一致就认定消息有效。例如可以使用MD5对some message + secret-key摘要式地生成一个MAC。

A–>B hello
B–>A Hi, I’m Bob, bobs-certificate
A–>B prove it
B–>A Alice, This Is Bob{digest[Alice, This Is Bob] } bobs-private-key
A–>B ok bob, here is a secret {secret} bobs-public-key
(B: MAC = Digest[some message, secret])
B–>A {some message,MAC}secret-key

TLS

TLS的维基百科有满满的介绍,包括各个版本的更新点。

从技术上讲,TLS 1.0与SSL 3.0的差异非常微小。但正如RFC所述

the differences between this protocol and SSL 3.0 are not dramatic, but they are significant enough to preclude interoperability between TLS 1.0 and SSL 3.0.

本协议和SSL 3.0之间的差异并不是显著,却足以排除TLS 1.0和SSL 3.0之间的互操作性。

TLS 1.0包括可以降级到SSL 3.0的实现,这削弱了连接的安全性。

2014年10月,Google发布在SSL 3.0中发现设计缺陷,建议禁用此一协议。攻击者可以向TLS发送虚假错误提示,然后将安全连接强行降级到过时且不安全的SSL 3.0,然后就可以利用其中的设计漏洞窃取敏感信息。Google在自己公司相关产品中陆续禁止向后兼容,强制使用TLS协议。Mozilla也在11月25日发布的Firefox 34中彻底禁用了SSL 3.0。微软同样发出了安全通告。

TLS与SSL的差异

  • 版本号:TLS记录格式与SSL记录格式相同,但版本号的值不同,TLS的版本1.0使用的版本号为SSLv3.1;
  • 报文鉴别码:SSLv3.0和TLS的MAC算法及MAC计算的范围不同。TLS使用了RFC-2104定义的HMAC算法;SSLv3.0使用了相似的算法,两者差别在于SSLv3.0中,填充字节与密钥之间采用的是连接运算,而HMAC算法采用的是异或运算。但是两者的安全程度是相同的;
  • 伪随机函数:TLS使用了称为PRF的伪随机函数来将密钥扩展成数据块,是更安全的方式;
  • 报警代码:TLS支持几乎所有的SSLv3.0报警代码,而且TLS还补充定义了很多报警代码,如解密失败(decryption_failed)、记录溢出(record_overflow)、未知CA(unknown_ca)、拒绝访问(access_denied)等;
  • 密文族和客户证书:SSLv3.0和TLS存在少量差别,即TLS不支持Fortezza密钥交换、加密算法和客户证书;
  • certificate_verify和finished消息:SSLv3.0和TLS在用certificate_verify和finished消息计算MD5和SHA-1散列码时,计算的输入有少许差别,但安全性相当;
  • 加密计算:TLS与SSLv3.0在计算主密值(master secret)时采用的方式不同;
  • 填充:用户数据加密之前需要增加的填充字节。在SSL中,填充后的数据长度要达到密文块长度的最小整数倍。而在TLS中,填充后的数据长度可以是密文块长度的任意整数倍(但填充的最大长度为255字节),这种方式可以防止基于对报文长度进行分析的攻击。

TLS的主要目标

就是使SSL更安全,并使协议的规范更精确和完善。

  • 更安全的MAC算法;
  • 更严密的警报;
  • “灰色区域”规范的更明确的定义。

TLS对于安全性的改进

  • 对于消息认证使用密钥散列法:TLS 使用“消息认证代码的密钥散列法”(HMAC),当记录在开放的网络(如因特网)上传送时,该代码确保记录不会被变更。SSLv3.0还提供键控消息认证,但HMAC比SSLv3.0使用的(消息认证代码)MAC 功能更安全;
  • 增强的伪随机功能(PRF):PRF生成密钥数据。在TLS中,HMAC定义PRF。PRF使用两种散列算法保证其安全性。如果任一算法暴露了,只要第二种算法未暴露,则数据仍然是安全的;
  • 改进的已完成消息验证:TLS和SSLv3.0都对两个端点提供已完成的消息,该消息认证交换的消息没有被变更。然而,TLS将此已完成消息基于PRF和HMAC值之上,这也比SSLv3.0更安全;
  • 一致证书处理:与SSLv3.0不同,TLS试图指定必须在TLS之间实现交换的证书类型;
  • 特定警报消息:TLS提供更多的特定和附加警报,以指示任一会话端点检测到的问题。TLS还对何时应该发送某些警报进行记录。

证书

证书分为2类:

  • 自签名证书
  • CA证书

自签名证书一般不能用来进行身份认证,如果一个server端使用自签名证书,client端要么被设置为无条件信任任何证书,要么需要将自签名证书的公钥和私钥加入受信任列表。

CA 根证书。数字证书认证机构(Certificate Authority, CA)签署和管理的 CA 根证书,会被纳入到你的浏览器和操作系统的可信证书列表中,并由这个列表判断根证书是否可信。信任链中如果只含有有效证书并且以可信锚点(trusted anchor)结尾,那么这个证书就被认为是有效的。例如 GlobalSign Root CA 。

花钱购买证书机构的签名。受信任的机构就可以用自己的私钥(sign.key)对其他人的证书进行签名。我们看到,只需要把证书请求(ssl.csr)发给证书机构,证书机构就可以生成出签名过的证书(ssl.crt)。目前购买证书签名服务的价格大约为100-400元/年。

像我们平时创建苹果开发者证书的时候,需要在开发者的电脑上通过钥匙串中证书助理处的从证书颁发机构请求证书获取.certSigningRequest,然后上传到苹果的develop后台去生成.cer,其实质就是请求苹果信任的 CA 对开发者的证书进行签名,使开发者的证书合法。

苹果develop后台创建.mobileprovision时,会将.cer的公钥附带上。在打包或者真机调试时,Xcode就会通过Bundle ID查找对应的.mobileprovision,然后利用Xcode指定的.cer的私钥加密生成一个签名去给.mobileprovision的公钥解密验证电脑的合法性。而.cer是不包含私钥的,.cer无法让别的电脑上正常地进行打包或者真机调试,但因为在创建.certSigningRequest时,证书源电脑本来持有的对应的私钥一直存在于其keychain上,所以证书源电脑能够正常工作。解决方法是将.cer导成.p12后给别的电脑安装,因为.p12里包含了公钥和私钥(安装了.p12后,别的电脑上的keychain中显示的cer就有了私钥)。

证书链

数字证书的生成是分层级的,下一级的证书需要其上一级证书的私钥签名的。所以后者是前者的证书颁发者,也就是说上一级证书的 Subject Name 是其下一级证书的 Issuer Name。

  • server发送证书给client认证身份;
  • client根据证书中的颁发者信息找该证书的上一级证书,然后验证该证书的合法性(即将该证书中的信息按其上级的签名算法计算签名,和使用上级证书的公钥解密该证书中的数字签名的原文作比较);
  • 若合法,则继续历遍验证证书链中的所有证书,直到数字证书认证机构(Certificate Authority, CA)签署和管理的 CA 根证书为止,则认为证书合法。

X.509 应该是比较流行的 SSL 数字证书标准,包含(但不限于)以下的字段:

字段 值说明
对象名称(Subject Name) 用于识别该数字证书的信息
共有名称(Common Name) 对于客户证书,通常是相应的域名
证书颁发者(Issuer Name) 发布并签署该证书的实体的信息
签名算法(Signature Algorithm) 签名所使用的算法
序列号(Serial Number) 由CA给予每一个证书分配的唯一的数字型编号
数字证书机构(Certificate Authority, CA) 给证书的唯一整数,一个数字证书一个序列号
生效期(Not Valid Before)
失效期(Not Valid After)
公钥(Public Key) 可公开的密钥
签名(Signature) 通过签名算法计算证书内容后得到的数据,用于验证证书是否被篡改

ATS(AppTransportSecurity)

ATS默认的条件

  • 服务器TLS版本至少是1.2版本
  • 连接加密只允许几种先进的加密

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

  • 证书必须使用SHA256或者更好的哈希算法进行签名,要么是2048位或者更长的RSA密钥,要么就是256位或更长的ECC密钥。

server需要提供HTTPS(TLS 1.2)的接口,可以在终端使用下面的命令验证server的API是否符合ATS标准。

nscurl --ats-diagnostics https://www.google.com

ATS的Client配置

在Xcode7或以上的版本中,新建的项目都默认使用ATS,即是访问的链接必须支持且使用HTTPS发起请求,否则将返回 的错误。

如果server还不支持ATS,则需要在app的info.plist中做一些如下的调整,使app能正常请求HTTP。

以下是info.plist里面与ATS有关的属性(分层级)

NSAppTransportSecurity
	NSAllowsArbitraryLoads
	NSExceptionDomains
		< domain-name-for-exception-as-string >
			NSExceptionMinimumTLSVerion
			NSExceptionRequiresForwardSecrecy
			NSExceptionAllowsInsecureHTTPLoads
			NSIncludesSubdomains
			NSThirdPartyExceptionMinimumTLSVersion
			NSThirdPartyExceptionRequiresForwardSecrecy
			NSThirdPartyExceptionAllowsInsecureHTTPLoads
  • NSAppTransportSecurity : 一个用于配置App Transport Security行为的属性,在Info.plist中是于Bundle Identifier同一级别的属性。
  • NSAllowsArbitraryLoads : 一个用于针对不在NSExceptionDomains中的域的配置项。如果设置成YES,则对于那些不在NSExceptionDomains的域则不需要通过ATS的验证。默认值是No,这时对于不在NSExceptionDomains里的域则是需要通过ATS的验证。
  • NSExceptionDomains : 用于配置例外的配置项。
  • < domain-name-for-exception-as-string > : 需要添加例外的域名字符串,如:www.baidu.com
  • NSExceptionMinimumTLSVerion : 用于指定例外域名的TSL的版本号,可用的配置有TLSv1.0、TLSv1.1以及TLSv1.2三个配置项。
  • NSExceptionRequiresForwardSecrecy : 用于指定所配置的域协议是否使用ATS的所要求的证书加密算法签名,如果是NO, 则加密算法必须是以下这几种。默认值是YES。

TLS_RSA_WITH_AES_256_GCM_SHA384
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_128_CBC_SHA

  • NSExceptionAllowsInsecureHTTPLoads : 用于指明所配置的域是否HTTPS的服务器。用这个配置可用访问那些没有证书、自签名证书、过期证书以及证书与域名匹配不上的服务器。默认值是NO。
  • NSIncludesSubdomains : 用于指明子域名是否使用同样的配置。默认值是NO。
  • NSThirdPartyExceptionMinimumTLSVersion : 该变量在域名为第三方域名时,且开发人员无法控制的情况下进行配置。
  • NSThirdPartyExceptionRequiresForwardSecrecy : 该变量在域名为第三方域名时,且开发人员无法控制的情况下进行配置。
  • NSThirdPartyExceptionRequiresForwardSecrecy : 该变量在域名为第三方域名时,且开发人员无法控制的情况下进行配置。

举例
使app请求的所有HTTP链接都能加载:

<key>NSAppTransportSecurity</key>
<dict>
    <!--Connect to anything (this is probably BAD)-->
    <key>NSAllowsArbitraryLoads</key><true/>
</dict>

使app请求的部分(指定域名的)HTTP或者低版本的HTTPS(TLS 1.1/1.0)链接能加载:

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key><true/>
  <key>NSExceptionDomains</key>
  <dict>
    <key>yourserver.com</key>
    <dict>
      <!--Include to allow subdomains-->
      <key>NSIncludesSubdomains</key><true/>
      <!--Include to allow insecure HTTP requests-->
      <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key><true/>
      <!--Include to allow securecy encrypt arithmetic-->
      <key>NSExceptionRequiresForwardSecrecy</key><true/>
      <!--Include to specify minimum TLS version-->
      <key>NSTemporaryExceptionMinimumTLSVersion</key><string>TLSv1.1</string>
    </dict>
  </dict>
</dict>

使app请求的指定域名的网址要经过ATS:

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key><true/>
  <key>NSExceptionDomains</key>
  <dict>
    <key>yourserver.com</key>
    <dict>
      <!--Include to allow subdomains-->
      <key>NSIncludesSubdomains</key><true/>
      <!--Include to allow insecure HTTP requests-->
      <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key><false/>
    </dict>
  </dict>
</dict>

自定义验证

服务端验证客户端

如果服务器的 response 中带有 WWW-Authenticate 的header信息,网络系统类就会调用它们的代理对象的代理方法去处理challenges。其它认证类型,例如 proxy authentication TLS trust validation 是不需要带上这个header也能触发。

在代码需要向认证的服务器请求资源时,服务器会使用http状态码401进行响应,即访问被拒绝需要验证。NSURLConnection会接收到响应并立刻使用认证challenge的一份副本来发送一条willSendRequestForAuthenticationChallenge:委托消息。过程如下所示:

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
	//以前的失败次数
	if ([challenge previousFailureCount] == 0) {
		//身份认证的类 
		NSURLCredential *newCredential;
		newCredential = [NSURLCredential credentialWithUser:@"账号" password:@"密码" persistence:NSURLCredentialPersistenceNone];
		[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
	} else {
		[[challenge sender] cancelAuthenticationChallenge:challenge];
	}
}

NSURLCredential代表的是一个身份验证证书。URL Loading系统支持3种类型的证书:password-based user credentials, certificate-based user credentials, and certificate-based server credentials。NSURLCredential适合大多数的认证请求,因为它可以表示由用户名/密码组合、客户端证书及服务器信任创建的认证信息。

为了认证,要创建一个含有服务端期望的认证信息的NSURLCredential对象。在系统提供的authentication challenge的protection space上调用 authenticationMethod 方法,可以获取服务器的认证方法。而NSURLCredential支持的authentication method有以下几种,可分别用括号中的NSURLCredential工厂方法创建对应的NSURLCredential对象。

  • NSURLAuthenticationMethodHTTPBasic (credentialWithUser:password:persistence)
  • NSURLAuthenticationMethodHTTPDigest(credentialWithUser:password:persistence)
  • NSURLAuthenticationMethodClientCertificate(credentialWithIdentity:certificates:persistence)
  • NSURLAuthenticationMethodServerTrust(credentialForTrust:)

客户端验证服务端

-URLSession:didReceiveChallenge:completionHandler: 回调中会收到一个 challenge,也就是质询,服务端需要你提供认证信息才能完成连接。

challenge 中还包含服务端提供的其自身的证明,可以通过 challenge.protectionSpace.authenticationMethod 取得保护空间要求我们认证的方式,如果这个值是 NSURLAuthenticationMethodServerTrust 的话,我们就可以插手 TLS 握手中“验证数字证书有效性”这一步,即是先验证服务端提供的身份,再返回客户端的认证信息给服务端建立SSL连接。

系统的默认实现(也即代理不实现这个方法)是验证这个信任链的,结果是有效的话则根据 serverTrust 创建 credential 用于同服务端确立 SSL 连接。否则会得到 “The certificate for this server is invalid…” 这样的错误而无法访问。

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(nonnull NSURLAuthenticationChallenge *)challenge completionHandler:(nonnull void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{
//以前的失败次数
	if ([challenge previousFailureCount] == 0) {
		NSURLCredential *credential = nil;
		NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;

		if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
			/* 调用自定义的验证过程 */ 
			SecTrustRef trust = challenge.protectionSpace.serverTrust;
			if ([self myCustomValidation:trust]) { 
				credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
				 if (credential) { 
				 	disposition = NSURLSessionAuthChallengeUseCredential; 
				 } 
			} else { 
				/* 无效的话,取消 */ 
				disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge 
			}
			!completionHandler?:completionHandler(disposition,credential)
		} else { 
			//身份认证的类 
			credential = [NSURLCredential credentialWithUser:@"账号" password:@"密码" persistence:NSURLCredentialPersistenceNone];
			//以下调用的效果同执行completionHandler一样
			[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; 
		}
	} else {
		[[challenge sender] cancelAuthenticationChallenge:challenge]; 
	}
}

自定义的验证过程中,需要先读取challenge的protectionSpace
属性的serverTrust属性,serverTrust 是一个 SecTrustRef 对象,它是一种执行信任链验证的抽象实体,包含着验证策略(SecPolicyRef)以及一系列受信任的锚点证书,而我们能做的也是修改这两样东西而已。

static BOOL myCustomValidation(SecTrustRef trust) {
	BOOL allowConnection = NO;
        
	// 假设验证结果是无效的
	SecTrustResultType trustResult = kSecTrustResultInvalid;
        
	// 函数的内部递归地从叶节点证书到根证书的验证
	OSStatus statue = SecTrustEvaluate(trust, &trustResult);
        
	if (statue == noErr) {
		// kSecTrustResultUnspecified: 系统隐式地信任这个证书,即在证书链中找到 CA 颁发的证书
		// kSecTrustResultProceed: 用户加入自己的信任锚点,显式地告诉系统这个证书是值得信任的
		allowConnection = (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified);
	}

return allowConnection;
}

打印 policiesRef 后,你会发现默认的验证策略就包含了域名验证,即服务器证书上的域名和请求域名是否匹配。如果你的一个证书需要用来连接不同域名的主机,或者你直接用 IP 地址去连接,那么你可以重设验证策略以忽略域名验证:

CFArrayRef policiesRef;
//打印出CFArrayRef会发现验证策略中默认是包含域名认证
SecTrustCopyPolicies(trust, &policiesRef);

NSMutableArray *policies = [NSMutableArray array]; 
// BasicX509 不验证域名是否相同
SecPolicyRef policy = SecPolicyCreateBasicX509();
[policies addObject:(__bridge_transfer id)policy]; 
SecTrustSetPolicies(trust, (__bridge CFArrayRef)policies);

然后再调用 myCustomValidation() 验证,SecTrustEvaluate函数就会根据trust的策略重新评估验证结果。

自签名证书验证

想要 App 防止中间人攻击,可以将公钥证书打包进 App 中,然后在收到服务端证书链的时候,能够有效地验证服务端是否可信,这也是验证自签名的证书链所必须做的。

假设你的服务器返回:[你的自签名的根证书] – [你的二级证书] – [你的客户端证书],系统是不信任这个三个证书的。所以你在验证的时候需要将这三个的其中一个设置为锚点证书,当然,多个也行。

比如将 [你的二级证书] 作为锚点后,SecTrustEvaluate() 函数只要验证到 [你的客户端证书] 确实是由 [你的二级证书] 签署的,那么验证结果为 kSecTrustResultUnspecified,表明了 [你的客户端证书] 是可信的。下面是设置锚点证书的做法:

NSMutableArray *certificates = [NSMutableArray array]; 
NSDate *cerData = /* 在 App Bundle 中你用来做锚点的证书数据,证书是 CER 编码的,常见扩展名有:cer, crt...*/ 
SecCertificateRef cerRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)cerData); 
[certificates addObject:(__bridge_transfer id)cerRef]; 
// 设置锚点证书。 
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)certificates);

然后再调用 myCustomValidation() 验证。

只调用 SecTrustSetAnchorCertificates () 这个函数的话,那么就只有作为参数被传入的证书作为锚点证书,连系统本身信任的 CA 证书都不能作为锚点验证的证书链。要想恢复系统中 CA 证书作为锚点的功能,还要再调用下面这个函数:

// true 代表仅被传入的证书作为锚点,false 允许系统 CA 证书也作为锚点
SecTrustSetAnchorCertificatesOnly(trust, false);

AFN的相关属性

AFSecurityPolicy,内部有三个重要的属性,如下:

  • AFSSLPinningMode SSLPinningMode; //该属性标明了AFSecurityPolicy是以何种方式来验证
  • BOOL allowInvalidCertificates; //是否允许不信任的证书通过验证,默认为NO
  • BOOL validatesDomainName; //是否验证主机名,默认为YES

AFSSLPinningMode枚举类型有三个值,分别是:

  • AFSSLPinningModeNone代表了AFSecurityPolicy不做更严格的验证,”只要是系统信任的证书”就可以通过验证,不过,它受到allowInvalidCertificatesvalidatesDomainName的影响;
  • AFSSLPinningModePublicKey是通过比较证书当中公钥(PublicKey)部分来进行验证,通过SecTrustCopyPublicKey方法获取本地证书和服务器证书,然后进行比较,如果有一个相同,则通过验证,此方式主要适用于自建证书搭建的HTTPS服务器和需要较高安全要求的验证;
  • AFSSLPinningModeCertificate则是直接将本地的证书设置为信任的根证书,然后来进行判断,并且比较本地证书的内容和服务器证书内容是否相同,来进行二次判断,此方式适用于较高安全要求的验证。

如果HTTPS服务器满足ATS默认的条件,而且SSL证书是通过权威的CA机构认证过的,那么什么都不用做。如果上面的条件中有任何一个不成立,那么都只能修改ATS配置。

小结

完成这篇文章后,才发现自己之前对网路编程的认识有多显浅。也才真正发现总结出一篇文章的好处,整理知识更系统(当然还要看文章的排版分类),而且大脑的记忆有限,有了文章日后回顾也方便直接。反正有意义有价值的的事都比较折腾,贵在坚持!

参考文章
SSL的工作原理
SSL与TLS区别
iOS 中对 HTTPS 证书链的验证
iOS9 - NSAppTransportSecurity
iOS9增加的ATS特性介绍
Authentication Challenges and TLS Chain Validation
Overriding TLS Chain Validation Correctly


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 mingfungliu@gmail.com

文章标题:ATS详解

文章字数:6.1k

本文作者:Mingfung

发布时间:2017-07-22, 15:57:55

最后更新:2019-10-10, 18:22:50

原始链接:http://blog.ifungfay.com/uncategorized/ATS详解/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏

宝贝回家