文章目录

  • 原理
    • 单向认证流程
    • 双向认证流程
  • 证书生成
    • 生成自签名根证书
    • 生成自签名服务器端证书
    • 生成自签名客户端证书
  • AFNetworking对于证书的校验机制

原理

双向认证,顾名思义,客户端和服务器端都需要验证对方的身份,在建立Https连接的过程中,握手的流程比单向认证多了几步。
单向认证的过程,客户端从服务器端下载服务器端公钥证书进行验证,然后建立安全通信通道。
双向通信流程,客户端除了需要从服务器端下载服务器的公钥证书进行验证外,还需要把客户端的公钥证书上传到服务器端给服务器端进行验证,等双方都认证通过了,才开始建立安全通信通道进行数据传输。

单向认证流程

单向认证流程中,服务器端保存着公钥证书和私钥两个文件,整个握手过程如下:

  1. 客户端发起建立HTTPS连接请求,将SSL协议版本的信息发送给服务器端;
  2. 服务器端将本机的公钥证书(server.crt)发送给客户端;
  3. 客户端读取公钥证书(server.crt),取出了服务端公钥;
  4. 客户端生成一个随机数(密钥R),用刚才得到的服务器公钥去加密这个随机数形成密文,发送给服务端;
  5. 服务端用自己的私钥(server.key)去解密这个密文,得到了密钥R
  6. 服务端和客户端在后续通讯过程中就使用这个密钥R进行通信了。

双向认证流程

  1. 客户端发起建立HTTPS连接请求,将SSL协议版本的信息发送给服务端;
  2. 服务器端将本机的公钥证书(server.crt)发送给客户端;
  3. 客户端读取公钥证书(server.crt),取出了服务端公钥;
  4. 客户端将客户端公钥证书(client.crt)发送给服务器端;
  5. 服务器端解密客户端公钥证书,拿到客户端公钥;
  6. 客户端发送自己支持的加密方案给服务器端;
  7. 服务器端根据自己和客户端的能力,选择一个双方都能接受的加密方案,使用客户端的公钥加密后发送给客户端;
  8. 客户端使用自己的私钥解密加密方案,生成一个随机数R,使用服务器公钥加密后传给服务器端;
  9. 服务端用自己的私钥去解密这个密文,得到了密钥R
  10. 服务端和客户端在后续通讯过程中就使用这个密钥R进行通信了。

证书生成

从上一章内容中,我们可以总结出来,如果要把整个双向认证的流程跑通,最终需要五个证书文件:

服务器端公钥证书:server.crt
服务器端私钥文件:server.key
客户端公钥证书:client.crt
客户端私钥文件:client.key
客户端集成证书(包括公钥和私钥,用于浏览器访问场景):client.p12
生成这一些列证书之前,我们需要先生成一个CA根证书,然后由这个CA根证书颁发服务器公钥证书和客户端公钥证书。

我们可以全程使用openssl来生成一些列的自签名证书,自签名证书没有听过证书机构的认证,很多浏览器会认为不安全,但我们用来实验是足够的。需要在本机安装了openssl后才能继续本章的实验。

生成自签名根证书

(1)创建根证书私钥:
openssl genrsa -out root.key 1024(2)创建根证书请求文件:
openssl req -new -out root.csr -key root.key
后续参数请自行填写,下面是一个例子:
Country Name (2 letter code) [XX]:cn
State or Province Name (full name) []:bj
Locality Name (eg, city) [Default City]:bj
Organization Name (eg, company) [Default Company Ltd]:alibaba
Organizational Unit Name (eg, section) []:test
Common Name (eg, your name or your servers hostname) []:www.yourdomain.com
Email Address []:a.alibaba.com
A challenge password []:
An optional company name []:(3)创建根证书:
openssl x509 -req -in root.csr -out root.crt -signkey root.key -CAcreateserial -days 3650

在创建证书请求文件的时候需要注意三点,下面生成服务器请求文件和客户端请求文件均要注意这三点:

Common Name填写证书对应的服务域名;
所有字段的填写,根证书、服务器端证书、客户端证书需保持一致
最后的密码可以直接回车跳过。
经过上面三个命令行,我们最终可以得到一个签名有效期为10年的根证书root.crt,后面我们可以用这个根证书去颁发服务器证书和客户端证书。

生成自签名服务器端证书

(1)生成服务器端证书私钥:
openssl genrsa -out server.key 1024(2) 生成服务器证书请求文件,过程和注意事项参考根证书,本节不详述:
openssl req -new -out server.csr -key server.key(3) 生成服务器端公钥证书
openssl x509 -req -in server.csr -out server.crt -signkey server.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650

经过上面的三个命令,我们得到:

server.key:服务器端的秘钥文件
server.crt:有效期十年的服务器端公钥证书,使用根证书和服务器端私钥文件一起生成

生成自签名客户端证书

(1)生成客户端证书秘钥:
openssl genrsa -out client.key 1024(2) 生成客户端证书请求文件,过程和注意事项参考根证书,本节不详述:
openssl req -new -out client.csr -key client.key(3) 生客户端证书
openssl x509 -req -in client.csr -out client.crt -signkey client.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650(3) 生客户端p12格式证书
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12

经过上面的三个命令,我们得到:

client.key:客户端的私钥文件
client.crt:有效期十年的客户端证书,使用根证书和客户端私钥一起生成
client.p12:客户端p12格式,这个证书文件包含客户端的公钥和私钥,主要用来给浏览器访问使用

AFNetworking对于证书的校验机制

AFNetworking解耦后可以分为以下几个模块:

  1. NSURLSession:主要的一个基于NSURLSession的管理模块;
  2. Reachability:网络监测模块;
  3. Security:Https验证模块;
  4. Serialization:序列化模块,包含了请求和响应的序列化;
  5. UIKit:包含了一些UI的扩展,方便调用。

AFNetworking关于https证书校验的逻辑,主要在源代码AFSecurityPolicy中实现。

AFNetworking提供了三个不同的认证模式,分别是:

typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {AFSSLPinningModeNone,AFSSLPinningModePublicKey,AFSSLPinningModeCertificate,
};
  • AFSSLPinningModeNone

代表无条件信任服务器的证书,这个模式表示不做SSL pinning,只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书。若证书是信任机构签发的就会通过,若是自己服务器生成的证书,这里是不会通过的。

核心校验相关代码

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);if (self.SSLPinningMode == AFSSLPinningModeNone) {return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust)}

核心代码主要是:

SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);//设置校验的参考依据
AFServerTrustIsValid(serverTrust); //系统API,基于上述的参考依据,得到证书的最终校验结果。

  • AFSSLPinningModePublicKey模式

代表会对服务器返回的证书中的PublicKey进行验证,客户端要有服务端的证书拷贝,验证时只验证证书里的公钥,不验证证书的有效期等信息。只要公钥是正确的,就能保证通信不会被窃听,因为中间人没有私钥,无法解开通过公钥加密的数据。

1.客户端本地内置证书白名单。获取访问域名证书链上的每一级证书的公钥 校验公钥是否正确。
核心代码:

 //只验证公钥NSUInteger trustedPublicKeyCount = 0;// 从serverTrust中取出服务器端传过来的所有可用的证书,并依次得到相应的公钥NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);//遍历服务端公钥for (id trustChainPublicKey in publicKeys) {for (id pinnedPublicKey in self.pinnedPublicKeys) {if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)){//判断如果相同 trustedPublicKeyCount+1trustedPublicKeyCount += 1;}}}return trustedPublicKeyCount > 0;
  • AFSSLPinningModeCertificate模式。

代表会对服务器返回的证书同本地证书全部进行校验,需要客户端保存有服务端的证书拷贝,这里验证分两步,第一步验证证书的域名/有效期等信息,第二步是对比服务端返回的证书跟客户端返回的是否一致。

1.获取https证书链上的每一级证书,将各个 证书都作为校验的参考依据。(简单说就是会校验整个证书链)

核心代码:

 //全部校验(nsbundle .cer)NSMutableArray *pinnedCertificates = [NSMutableArray array];
//把证书data,用系统api转成 SecCertificateRef 类型的数据,SecCertificateCreateWithData函数对原先的pinnedCertificates做一些处理,保证返回的证书都是DER编码的X.509证书
for (NSData *certificateData in self.pinnedCertificates) {[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
// 将pinnedCertificates设置成需要参与验证的Anchor Certificate(锚点证书,通过SecTrustSetAnchorCertificates设置了参与校验锚点证书之后,假如验证的数字证书是这个锚点证书的子节点,即验证的数字证书是由锚点证书对应CA或子CA签发的,或是该证书本身,则信任该证书),具体就是调用SecTrustEvaluate来验证//serverTrust是服务器来的验证,有需要被验证的证书// 把本地证书设置为根证书,SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
//再去验证证书if (!AFServerTrustIsValid(serverTrust)) {return NO;}//注意,这个方法和我们之前的锚点证书没关系了,是去从我们需要被验证的服务端证书,去拿证书链。// 服务器端的证书链,注意此处返回的证书链顺序是从叶节点到根节点// 所有服务器返回的证书信息NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {//如果我们的证书中,有一个和它证书链中的证书匹配的,就返回YES// 是否本地包含相同的dataif ([self.pinnedCertificates containsObject:trustChainCertificate]) {return YES;}}return NO;

SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); //增加了新的校验维度,客户端本地证书白名单

2.要求客户端本地的内置证书白名单里至少包含一个证书链里的证书。

 NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);            NSUInteger trustedCertificateCount = 0;for (NSData *trustChainCertificate in serverCertificates) {if ([self.pinnedCertificates containsObject:trustChainCertificate]) {trustedCertificateCount++;}}return trustedCertificateCount > 0;

NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); //得到访问域名的证书链上的各个证书信息

AFSSLPinningModeCertificate对应的则是证书锁定

AFSSLPinningModeCertificate缺点

客户端内置证书有有效期,证书有效期过后,需要强制升级客户端
发行APP比较麻烦,每次证书需要打包在APP中,服务器证书到期后则要重新内置证书后打包发行

iOS https证书双向认证的实现机制相关推荐

  1. nginx配置https证书双向认证

    目标 使用openssl生成证书并配置到nginx. 步骤 1. 生成服务端证书和客户端证书 2. 配置nginx并重启 3. 本地浏览器导入客户端证书 4. 测试验证 证书制作详细过程 # 1 创建 ...

  2. HTTPS 中双向认证SSL 协议的具体过程

    HTTPS 中双向认证SSL 协议的具体过程: 这里总结为详细的步骤: ① 浏览器发送一个连接请求给安全服务器. ② 服务器将自己的证书,以及同证书相关的信息发送给客户浏览器. ③ 客户浏览器检查服务 ...

  3. http(S)系列之(三):https单/双向认证区别

    前言 没有梗了,挖槽,不是朕的风格,硬来一个:昨晚做梦妻妾成群,年收入超千万了,可惜被儿子一泡尿滋醒了 参考文献 扯一扯HTTPS单向认证.双向认证.抓包原理.反抓包策略 提醒:参考文献里面涉及到单向 ...

  4. https的双向认证

    https的双向认证

  5. yum https ssl双向认证 证书申请

    本文将着重介绍从ssl证书申请到搭建yum使用https双向认证完成rpm包安装的流程,具体https双向认证的原理在这不做详细的描述,认证的详细描述的原理可以参考: https://blog.csd ...

  6. firefox+linux+nginx搭建server与client通过证书双向认证环境

    项目中需要搭建一个server和client基于证书的双向认证环境.由我来做,我也不会. 经过一晚上的研究,基本摸清了(知其然不知其所以然).做下笔记. 基本环境: 1.安装nginx. 2.安装op ...

  7. nginx配置反向代理验证ssl证书 双向认证

    适用于公司内部一些业务系统对安全性要求比较高,业务系统只允许公司内部人员访问,而且要求浏览器要安装证书登录,对公司入职有需求的人员开通证书,流失的人员注销证书. 修改openssl配置参数 vim / ...

  8. iOS HTTPS证书不受信任解决办法

    之前开发App的时候服务端使用的是自签名的证书,导致iOS开发过程中调用HTTPS接口时,证书不被信任 - (void)URLSession:(NSURLSession *)session didRe ...

  9. mysql 证书双向认证_https证书双向验证

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

最新文章

  1. [Config]如何利用ConfigurationSettings.AppSettings.GetValues读取配置文件中多个同Key的value...
  2. 视频监控软件 SecuritySpy 简介
  3. 【源码解读】Screencap源码分析-基础篇
  4. 【LeetCode笔记】88. 合并两个有序数组(Java、双指针)
  5. django-中间件,视图函数调用前会执行的
  6. SpringBoot (14)---使用Redis缓存
  7. 给定一个序列,判断该序列是否为二叉树查找树的后序遍历序列
  8. vue从后台下载.zip压缩包文件
  9. 【无标题】CentOS7 安装 向日葵 远程工具
  10. 《畅玩NAS》 使用树莓派打造一个NAS服务器
  11. 图灵奖Alan Kay:突破常规思维!道翰天琼认知智能机器人平台API接口大脑为您揭秘
  12. 关于打开github网站慢如何解决
  13. python base_Python base(一)
  14. 前端js拼接Json串
  15. 机器学习 | k近邻
  16. 元宇宙,小荷才露尖尖角
  17. oracle xe 安装配置,(转)oracle 10g xe 我的安装实践及简单配置过程
  18. React Native 开源项目汇总
  19. c语言整点报时,C语言编写一个简单整点报时工具源代码
  20. 【程序人生】卡塔尔世界杯元素python海龟绘图(附源代码),世界杯主题前端特效5个(附源码)

热门文章

  1. 架构师日常-团队管理
  2. 关于tensorflow的报错NodeDef mentions attr ‘xxx‘ not in Op的解决方案和产生原因
  3. echarts实现自定义扩展地图-中国七大区域图
  4. 数据结构之不带头结点单链表和带头结点单链表相关操作实现(C语言)
  5. 网站被流量攻击了,如何解决
  6. 启发式函数在A* 中的作用
  7. java 高级面试题(借鉴)(上)
  8. DB2 V9.7新特性 - 降低高水位标记
  9. cannot delete inactive domain with nvram
  10. 小森生活服务器维护公告,小森生活2021年5月11日停服更新公告