这篇文章是我一边学习证书验证一边记录的内容,
稍微整理了下,共扯了三部分内容:

  1. HTTPS 简要原理;
  2. 数字证书的内容、生成及验证;
  3. iOS 上对证书链的验证。

HTTPS 概要

HTTPS 是运行在 TLS/SSL 之上的 HTTP,与普通的 HTTP 相比,在数据传输的安全性上有很大的提升。
要了解它安全性的巧妙之处,需要先简单地了解对称加密非对称加密的区别:

  • 对称加密只有一个密钥,加密和解密都用这个密钥;
  • 非对称加密有公钥和私钥,私钥加密后的内容只有公钥才能解密,公钥加密的内容只有私钥才能解密。

为了提高安全性,我们常用的做法是使用对称加密的手段加密数据。可是只使用对称加密的话,双方通信的开始总会以明文的方式传输密钥。那么从一开始这个密钥就泄露了,谈不上什么安全。所以 TLS/SSL 在握手的阶段,结合非对称加密的手段,保证只有通信双方才知道对称加密的密钥。大概的流程如下:

TSL:SSL_handshake.png

所以,HTTPS 实现传输安全的关键是:在 TLS/SSL 握手阶段保证仅有通信双方得到 Session Key!

数字证书的内容

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

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

除了上述所列的字段,还有很多拓展字段,在此不一一详述。

下图为 Wikipedia 的公钥证书:

wikipedia_cer.png

数字证书的生成及验证

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

在得到证书申请者的一些必要信息(对象名称,公钥私钥)之后,证书颁发者通过 SHA-256 哈希得到证书内容的摘要,再用自己的私钥给这份摘要加密,得到数字签名。综合已有的信息,生成分别包含公钥和私钥的两个证书。

扯到这里,就有几个问题:

问:如果说发布一个数字证书必须要有上一级证书的私钥加密,那么最顶端的证书——根证书怎么来的?

根证书是自签名的,即用自己的私钥签名,不需要其他证书的私钥来生成签名。

问:怎么验证证书是有没被篡改?

当客户端走 HTTPS 访问站点时,服务器会返回整个证书链。以下图的证书链为例:

chain_hierarchy.png

要验证 *.wikipedia.org 这个证书有没被篡改,就要用到 GlobalSign Organization Validation CA - SHA256 - G2 提供的公钥解密前者的签名得到摘要 Digest1,我们的客户端也计算前者证书的内容得到摘要 Digest2。对比这两个摘要就能知道前者是否被篡改。后者同理,使用 GlobalSign Root CA 提供的公钥验证。当验证到到受信任的根证书时,就能确定 *.wikipedia.org 这个证书是可信的。

问:为什么上面那个根证书 GlobalSign Root CA受信任的

数字证书认证机构(Certificate Authority, CA)签署和管理的 CA 根证书,会被纳入到你的浏览器和操作系统的可信证书列表中,并由这个列表判断根证书是否可信。所以不要随便导入奇奇怪怪的根证书到你的操作系统中。

问:生成的数字证书(如 *.wikipedia.org)都可用来签署新的证书吗?

不一定。如下图,拓展字段里面有个叫 Basic Constraints 的数据结构,里面有个字段叫路径长度约束(Path Length Constraint),表明了该证书能继续签署 CA 子证书的深度,这里为0,说明这个 GlobalSign Organization Validation CA - SHA256 - G2 只能签署客户端证书,而客户端证书不能用于签署新的证书,CA 子证书才能这么做。

path_length_constraint.png

iOS 上对证书链的验证

Overriding TLS Chain Validation Correctly 中提到:

When a TLS certificate is verified, the operating system verifies its chain of trust. If that chain of trust contains only valid certificates and ends at a known (trusted) anchor certificate, then the certificate is considered valid.

所以在 iOS 中,证书是否有效的标准是:

信任链中如果只含有有效证书并且以可信锚点(trusted anchor)结尾,那么这个证书就被认为是有效的。

其中可信锚点指的是系统隐式信任的证书,通常是包括在系统中的 CA 根证书。不过你也可以在验证证书链时,设置自定义的证书作为可信的锚点。

NSURLSession 实现 HTTPS

具体到使用 NSURLSession 走 HTTPS 访问网站,-URLSession:didReceiveChallenge:completionHandler: 回调中会收到一个 challenge,也就是质询,需要你提供认证信息才能完成连接。这时候可以通过 challenge.protectionSpace.authenticationMethod 取得保护空间要求我们认证的方式,如果这个值是 NSURLAuthenticationMethodServerTrust 的话,我们就可以插手 TLS 握手中“验证数字证书有效性”这一步。

默认的实现

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

比如在访问 https://www.google.com 的时候咧,我们不实现这个方法也能访问成功的。系统对 Google 服务器返回来的证书链,从叶节点证书往根证书层层验证(有效期、签名等等),遇到根证书时,发现作为可信锚点的它存在与可信证书列表中,那么验证就通过,允许与服务端建立连接。

google.png

而当我们访问 https://www.12306.cn 时,就会出现 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “www.12306.cn” which could put your confidential information at risk." 的错误。原因就是系统在验证到根证书时,发现它是自签名、不可信的。

12306.png

自定义实现

如果我们要实现这个代理方法的话,需要提供 NSURLSessionAuthChallengeDisposition(处置方式)和 NSURLCredential(资格认证)这两个参数给 completionHandler 这个 block:

 -(void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition,
NSURLCredential * _Nullable))completionHandler { // 如果使用默认的处置方式,那么 credential 就会被忽略
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
NSURLCredential *credential = nil; if ([challenge.protectionSpace.authenticationMethod
isEqualToString:
NSURLAuthenticationMethodServerTrust]) { /* 调用自定义的验证过程 */
if ([self myCustomValidation:challenge]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
}
} else {
/* 无效的话,取消 */
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}

[self myCustomValidation:challenge] 调用自定义验证过程,结果是有效的话才创建 credential 确立连接。
自定义的验证过程,需要先拿出一个 SecTrustRef 对象,它是一种执行信任链验证的抽象实体,包含着验证策略(SecPolicyRef)以及一系列受信任的锚点证书,而我们能做的也是修改这两样东西而已。

SecTrustRef trust = challenge.protectionSpace.serverTrust;

拿到 trust 对象之后,可以用下面这个函数对它进行验证。

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

这个函数什么时候调用完全取决于你的需求,如果你不想对验证策略做修改而直接调用的话,那你居然还看到这里!?(╯‵□′)╯︵┻━┻

域名验证

可以通过以下的代码获得当前的验证策略:

CFArrayRef policiesRef;

SecTrustCopyPolicies(trust, &policiesRef);

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

 NSMutableArray *policies = [NSMutableArray array];

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

后再调用 serverTrustIsVaild() 验证。

但是如果不验证域名的话,安全性就会大打折扣。拿浏览器举

iOS 中对 HTTPS 证书链的验证的更多相关文章

  1. ios 中使用https的知识

    先看文章,这篇文章说的是使用AFNetworing进行https时的事项,十分好!http://blog.cnbang.net/tech/2416/ ios中使用https,主要就是使用NSURLCr ...

  2. iOS 对 HTTPS 证书链的验证

    HTTPS从最终的数据解析的角度,与HTTP相同.HTTPS将HTTP协议数据包放到SSL/TSL层加密后,在TCP/IP层组成IP数据报去传输,以此保证传输数据的安全:而对于接收端,在SSL/TSL ...

  3. iOS对HTTPS证书链的验证原理

    今天看到所在的某个开发群问https原理,之前做HTTPS ,下面简单说下原理.希望能帮助你理解. HTTPS从最终的数据解析的角度,与HTTP相同.HTTPS将HTTP协议数据包放到SSL/TSL层 ...

  4. iOS 中 AFNetworking HTTPS 的使用

    由于我们公司由HTTP转HTTPS,出现了一系列问题特此记录下. 一.HTTPS 二.App Transport Security 三.iOS 中用HTTPS 注意的问题 四.使用 AFNetwork ...

  5. IOS 中使用token机制来验证用户的安全性

    登录的业务逻辑{    http:是短连接.         服务器如何判断当前用户是否登录?    // 1. 如果是即时通信类:长连接.    // 如何保证服务器跟客户端保持长连接状态? // ...

  6. iOS中的HTTPS

    在WWDC 15,Apple提出的ATS (App Transport Security) ,是 Apple 在推进网络通讯安全的一个重要方式.在 iOS 9 和 OS X 10.11 中,默认情况下 ...

  7. iOS UIWebView 访问https 绕过证书验证的方法

    在文件开始实现  allowsAnyHTTPSCertificateForHost 方法 @implementation NSURLRequest (NSURLRequestWithIgnoreSSL ...

  8. iOS UIWebView 访问https绕过证书验证的方法

    @implementation NSURLRequest (NSURLRequestWithIgnoreSSL) + (BOOL)allowsAnyHTTPSCertificateForHost:(N ...

  9. 【腾讯Bugly干货分享】iOS 中 HTTPS 证书验证浅析

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/-fLLTtip509K6pNOTkflPQ 导语 本 ...

随机推荐

  1. 巧遇"drwxr-xr-x."权限

    drwxr-xr-x. 是SELinux的问题 REDHAT6之后安全提高所以设置的 关闭SELINUX就好了 因为新版本ls把多acl和selinux属性加进去了,与系统无关,新版本的ls代码使用1 ...

  2. SpringMVC 使用Form标签库制作登录表单

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...

  3. FJNU 1152 Fat Brother And Integer(胖哥与整数)

    FJNU 1152 Fat Brother And Integer(胖哥与整数) Time Limit: 1000MS   Memory Limit: 257792K [Description] [题 ...

  4. php 连接mysql的问题

    当前我的情况是安装上mysql配置了my.cnf中的socket,并且在指定位置也生成了 mysql.sock 的sock文件,此时我通过php.ini来设置php连接mysql.sock的文件,但是 ...

  5. Codeforces Round #286 (Div. 2) B. Mr. Kitayuta's Colorful Graph dfs

    B. Mr. Kitayuta's Colorful Graph time limit per test 1 second memory limit per test 256 megabytes in ...

  6. iOS企业版打包(转载)

    转自 http://www.cnblogs.com/shenlaiyaoshi/p/5472474.html   神来钥匙-陈诗友 iOS 企业版 打包 使用 iOS 企业版的证书发布应用可以跳过 A ...

  7. bootstrap学习笔记<五>(表单一)

    表单 bootstrap为表单提供三种样式:默认表单,水平表单,内联表单. <form class="form-horizontal" role="form&quo ...

  8. Mysql ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA

    ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declar ...

  9. Java编程思想学习笔记_4(异常机制,容器)

    一.finally语句注意的细节: 当涉及到break和continue语句的时候,finally字句也会得到执行. public class Test7 { public static void m ...

  10. jQuery学习笔记:attr()与prop()的区别

    先看看官方文档是如何解释两者之间功能差异的: attr() Get the value of an attribute for the first element in the set of matc ...