浅析SSL/TLS的会话流程和源码实现
浅析SSL/TLS的会话流程和源码实现
- 一、SSL/TLS的概念
- 二、SSL/TLS的会话交互流程
- (1) client_hello
- (2) server_hello + certificate + sever hello done
- (3) client key exchange + change cipher spec + encrypted handshake message
- (4) new session ticket+change cipher spec+envrypted handshake message
- 三、源码实现
一、SSL/TLS的概念
官方解释TLS叫做安全传输层协议(TLS)用于在两个通信应用程序之间提供保密性和数据完整性。SSL就是TLS的前身,从最初的SSL3到现在的TLS 1.3,其协议核心并没有大幅改变。只是为了解决几个安全性问题。TLS实际是在明文的上层和 TCP 层之间加上一层加密,这样就保证上层信息传输的安全,所以在网络模型中它运行于传输层之上,隶属于会话层。实际应用例如在浏览器访问中,在HTTP 协议中加上 SSL 层之后,就有了HTTPS。
二、SSL/TLS的会话交互流程
ssl实际应用中具体的交互流程如下图所示:
(1) client_hello
客户端发起协议交互请求,其中包含tls的版本信息,加密套件候选列表,随机数等相关信息,以我们的设备为例抓包来分析具体实况:
- tls的版本信息主要是当前客户端所支持的最高 TLS 协议版本 ,目前已有的TLS 协议版本总共有五种,从低到高依次 SSLv2, SSLv3, TLSv1, TLSv1.1, TLSv1.2
- 客户端所支持的加密套件cipher suites, 每个加密套件对应TLS 原理中的四个功能的组合:
- 认证算法 Au (身份验证)
- 密钥交换算法 KeyExchange (密钥协商)
- 对称加密算法 Enc (信息加密)
- 信息摘要 Mac (完整性校验)
- 随机数 random_C,用于后续的密钥的生成
(2) server_hello + certificate + sever hello done
- server_hello, 服务端返回协商的信息结果,包括选择使用的协议版本 version,选择的加密套件 cipher suite,选择的压缩算法 compression method、随机数 random_S 等,其中随机数用于后续的密钥协商
- server_certificates, 服务器端配置对应的证书链,用于身份验证与密钥交换
- server_hello_done,通知客户端 server_hello 信息发送结束
(3) client key exchange + change cipher spec + encrypted handshake message
- client_key_exchange: 合法性验证通过之后,客户端计算产生随机数字 pre-master,并用证书公钥加密,发送给服务器
- 此时客户端已经获取全部的计算协商密钥需要的信息:两个明文随机数 random_C 和 random_S 与自己计算产生的 pre-master,计算得到协商密钥enc_key=Fuc(random_C, random_S, pre-master)
- change_cipher_spec: 客户端通知服务器后续的通信都采用协商的通信密钥和加密算法进行加密通信;
- encrypted_handshake_message: 结合之前所有通信参数的 hash 值与其它相关信息生成一段数据,采用协商密钥 session secret 与算法进行加密,然后发送给服务器用于数据与握手验证
(4) new session ticket+change cipher spec+envrypted handshake message
- session ticket,SessionTicket是一种不需要服务器端状态的,恢复TLS session的方式。SessionTicket可以用于任何CipherSuite,服务器如果使用 SessionTicket 机制,服务器需要把本地的 session 状态存入一个ticket中,ticket会被加密,并被MAC保护,无法篡改,加密和算MAC用的key只有服务器知道。加密并MAC过的ticket用 NewSessionTicket 消息分发给客户端, 客户端把收到的ticket和master secret等其它与当前session有关的参数一起,缓存起来。当客户端希望恢复会话时,就把ticket包含在 ClientHello 的 SessionTicket 扩展中发给服务器。服务器收到后,解密ticket,算MAC确认ticket没有被篡改过,然后从解密的内容里面,获取session 状态,用来恢复会话。如果服务器成功地验证了ticket,可以在 ServerHello 之后返回一个 NewSessionTicket 消息来更新ticket。
- 服务器用私钥解密加密的 pre-master 数据,基于之前交换的两个明文随机数 random_C 和 random_S,计算得到协商密钥:enc_key=Fuc(random_C, random_S, pre-master); 计算之前所有接收信息的 hash 值,然后解密客户端发送的 encrypted_handshake_message,验证数据和密钥正确性;
- change_cipher_spec, 验证通过之后,服务器同样发送 change_cipher_spec 以告知客户端后续的通信都采用协商的密钥与算法进行加密通信;
- encrypted_handshake_message, 服务器也结合所有当前的通信参数信息生成一段数据并采用协商密钥 session secret 与算法加密并发送到客户端;
三、源码实现
这里针对ssl的交互流程,我们搭配openssl的库来实现一套具体的交互配置流程,前提是我们已经申请过了CA证书。
首先是ssl的参数CTX的创建,需要我们配置当前ssl的CA证书路径,验证链深度,加密套件等用于服务端的握手条件
SSL_CTX* config_ctx(HPR_VOID)
{HPR_INT32 sdwRet = HPR_ERROR;SSL_CTX *pCtx = NULL;SECURE_ENCRY_PARAM stSecureEncryParam ;pCtx = SSL_CTX_new(TLSv1_2_server_method());if (NULL == pCtx){ERR_print_errors_fp(stdout);SSL_COM_ERR("SSL_CTX_new fail\n");return NULL;}SSL_CTX_set_verify(pCtx, SSL_VERIFY_NONE, NULL); //不验证/** 设置证书验证链的深度 默认深度为 9*/SSL_CTX_set_verify_depth(pCtx, 9);/** 设置使用或者需要禁止的安全套件*/SSL_CTX_set_cipher_list(pCtx, "TLSv1:!LOW:!MEDIUM:!MD5:!ADH:!NULL:!DH");/** 设置证书加密的密码*/SSL_CTX_set_default_passwd_cb_userdata(pCtx, pwd);if (ssl_ctx_use_certificate_file_v2(pCtx, path, SSL_FILETYPE_PEM) <= 0) //path为CA证书,即sever.crt{ERR_print_errors_fp(stderr);SSL_COM_ERR("SSL_CTX_use_certificate_file fail\n",path);goto EXIT_PORT;}if (ssl_ctx_use_privatekey_file_v2(pCtx, PRI_KEY_FILE, SSL_FILETYPE_PEM) <= 0) //PRI_KEY_FILE为证书私钥{ERR_print_errors_fp(stderr);SSL_COM_ERR("ssl_ctx_use_privatekey_file_v2 %s fail\n", PRI_KEY_FILE);goto EXIT_PORT;}// 判断使用的证书秘钥对是否匹配有效if (!SSL_CTX_check_private_key(pCtx)){ERR_print_errors_fp(stderr);SSL_COM_ERR("Private key does not match the certificate public key\n");goto EXIT_PORT;}//安全地使用SSL_OP_ALL来启用错误解决方法选项//SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION 在作为服务器执行重新协商时,始终启动新会话(即,仅在初始握手中接受会话恢复请求)。客户端不需要此选项。*/SSL_CTX_set_options(pCtx, SSL_OP_ALL | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);/* SSL_CTX_set_options(pCtx, SSL_OP_NO_RENEGOTIATION); // 解决安全扫描出现的 TLS Client-initiated 重协商攻击(CVE-2011-1473) 漏洞, 禁用TLSv1.2及更早版本中的所有重新协商。不发送HelloRequest消息,并通过ClientHello忽略重新协商请求。, OpenSSL 1.1.1 版本开始支持, 1.1 之前使用 pSsl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS配置 */// 设置回掉函数,内部可以用来限制重协商的次数SSL_CTX_set_info_callback(pCtx, ssl_info_callback);SSL_CTX_set_mode(pCtx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_AUTO_RETRY);return pCtx;EXIT_PORT:/* This will never occur! */if(NULL != pCtx ){SSL_CTX_free(pCtx);pCtx = NULL;}return NULL;
}
其次是将fd的绑定到我们之前已经创建的ctx上,前面已经说过tls是工作在tcp之上,当我们创建一个tcp连接之后需要将fd绑定到ssl
SSL* bind_ssl_fd(HPR_INT32 connfd, SSL_CTX *ctx)
{SSL *pSsl = NULL;HPR_INT32 sdwRet = -1;if (NULL == ctx || -1 == connfd){return NULL;}/* 基于 ctx 产生SSL*/pSsl = SSL_new(ctx);if (NULL == pSsl){SSL_COM_ERR(" SSL_new fail \n");return NULL;}/* 将连接用户的 socket 加入SSL*/sdwRet = SSL_set_fd(pSsl, connfd);if (sdwRet == 0){SSL_COM_ERR(" SSL_set_fd fail ret =%d \n", sdwRet);goto EXIT_PORT; }/** 该函数若设置为 true, ssl 将会尽可能多地读取数据到ssl缓冲区, 若设置为false 则将只读取当前处理的record*/SSL_set_read_ahead(pSsl, false);/* SSL_set_accept_state(pSsl); */pSsl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;/*建立 SSL 连接*/sdwRet = SSL_accept(pSsl);if (sdwRet <= 0){ERR_print_errors_fp(stderr);SSL_COM_ERR("SSL_accept fail ret =%d \n", sdwRet);goto EXIT_PORT; }SSL_COM_INFO("server: Connected with %s encryption\n", SSL_get_cipher(pSsl));show_certs_info(pSsl);return pSsl;EXIT_PORT:if(NULL != pSsl){SSL_shutdown(pSsl);SSL_free(pSsl);pSsl = NULL;} return NULL;}
这里我们的ssl连接已经创建完毕,当我们接受或者需要写入数据时调用ssl的读写接口就可以对数据进行加密传输了
HPR_INT32 ssl_readn(SSL *ssl, HPR_INT8* buf, HPR_INT32 len, HPR_INT32 *pdwSslStoreLen)
{HPR_INT32 nLeft = len;HPR_INT32 recvLen = 0;HPR_INT32 dwSslStoreLen = 0;HPR_INT32 connfd = -1;HPR_INT32 err = -1;struct timeval select_timeout;fd_set rset;if(NULL == ssl || NULL == buf ){SSL_COM_ERR("err Param \n");return HPR_ERROR;}connfd = SSL_get_fd(ssl);if (-1 == connfd){SSL_COM_ERR("SSL_get_ssl fail\n");return HPR_ERROR;}FD_ZERO(&rset);FD_SET(connfd, &rset);while (nLeft > 0){select_timeout.tv_sec = 5;select_timeout.tv_usec = 0;/** @brief: select 仅可检测到链路层的可读数据, 对于已经缓存在ssl缓冲区但是未接收到应用层的数据 无法检测,所以用dwSslStoreLen进行储值判断*//** ssl 缓冲区没有数据才需要等待 select*/if(0 == dwSslStoreLen){if (select(connfd + 1, &rset, NULL, NULL, &select_timeout) <= 0){SSL_COM_ERR("readn: select failed, errno = 0x%x\n", errnoGet());return len - nLeft; }}do{/** @note: 无论SSL_read多少字节的数据(即便是0),SSL都会讲一个完整的recode 读进ssl缓冲区,这个recode的数据将不再存在于tcp层,无法被select到, SSL_read 再从ssl缓冲区读进应用层的buf */recvLen = SSL_read(ssl, buf + len - nLeft, nLeft);if (recvLen > 0){nLeft -= recvLen;}else{err = SSL_get_error(ssl, recvLen);/** @brief: fd暂时无数据可读*/if (SSL_ERROR_WANT_READ == err){recvLen = 0;SSL_COM_ERR("SSL_ERROR_WANT_READ, fd = %d , has recv %d len \n", connfd, len - nLeft);}/** @brief: 这里 ssl链路断开 就视为链路无效了,就退出链路*//** @brief: ssl链路断开, 注意: 这不一定代表 tcp 链路已经断开*/else if (SSL_ERROR_ZERO_RETURN == err){SSL_COM_ERR("SSL_ERROR_ZERO_RETURN, fd = %d, has recv %d len \n", connfd, len - nLeft); return len - nLeft; }else if (SSL_ERROR_SYSCALL == err){SSL_COM_ERR("SSL_ERROR_SYSCALL, fd = %d, has recv %d len \n", connfd, len - nLeft); SSL_COM_ERR ("错误代码%d,错误信息是'%s'\n", errno, strerror(errno));return len - nLeft; }else{SSL_COM_ERR("select exit, err = %d, fd = %d, has recv %d len \n",errno, connfd, len - nLeft); return len - nLeft; }}dwSslStoreLen = SSL_pending(ssl);}while (nLeft > 0 && dwSslStoreLen); /** 需要读取的数据还没读完, 且ssl缓存区还有数据*/}if (NULL != pdwSslStoreLen){*pdwSslStoreLen = dwSslStoreLen;}return len - nLeft;
}HPR_INT32 ssl_writen(SSL *ssl, HPR_INT8* buf, HPR_INT32 len)
{HPR_INT32 nLeft = len;HPR_INT32 sendLen = 0;HPR_INT32 err = 0;HPR_INT32 connfd = -1;if(NULL == ssl || NULL == buf ){SSL_COM_ERR("err Param \n"); return HPR_ERROR;}while (nLeft > 0){sendLen = SSL_write(ssl, buf + len - nLeft, nLeft);if (sendLen >= 0){nLeft -= sendLen;}else{/** @brief: fd暂时不可写*/err = SSL_get_error(ssl, sendLen);if (SSL_ERROR_WANT_WRITE == err){sendLen = 0;SSL_COM_ERR("SSL_ERROR_WANT_WRITE\n");}/** @brief: ssl链路断开, 注意: 这不一定代表 tcp 链路已经断开*/else if (SSL_ERROR_ZERO_RETURN == err){SSL_COM_ERR("SSL_ERROR_ZERO_RETURN\n");return len - nLeft; }else if (SSL_ERROR_SYSCALL == err){SSL_COM_ERR("SSL_ERROR_SYSCALL\n");SSL_COM_ERR("错误代码%d,错误信息是'%s'\n", errno, strerror(errno));return len - nLeft; }else{SSL_COM_ERR("select exit, err = %d\n", err);return len - nLeft; }}}return len - nLeft;
}
参考文档: https://blog.csdn.net/ustccw/article/details/76691248.
浅析SSL/TLS的会话流程和源码实现相关推荐
- SSL/TLS协议交互流程分析
本文参考 SSL/TLS协议运行机制的概述 tls运行机制,这里不细说,建议细看 HTTPS与TLS The Transport Layer Security (TLS) Protocol v1.2 ...
- OBS框架流程和源码分析七一视频流捕获机制
1. 视频流捕获机制 1.1. 视频流捕获基本原理 所谓视频流,实际上是由一张张图像组成,由于人体眼睛的捕获频率,以及视觉暂留机制,在图像连续播放时,会让大脑以为产生连贯性的动画效果.常见的电影帧率是 ...
- JavaScript中的经典题型(类数组、CSS Sprites、事件委托、经典去重、原型链、闭包、深浅克隆、附带思路流程和源码)
JavaScript中的经典题型 一.JavaScript中的经典题型 1..如何判断一个数组和类数组? 首先要明白什么是类数组. 类数组:类数组是一个普通对象,他的原型是Object.而真实的数组是 ...
- 第四篇:网络安全,SSL/TLS加密技术
文章目录 一.前言 二.SSL/TLS 2.1 SSL/TLS是什么 2.2 SSL/TLS加密基本原理 2.3 SSL/TLS建立握手过程 三.CA & SSL Server & S ...
- 蚂蚁区块链第9课 SSL/TLS工作原理及在蚂蚁BAAS中的应用
1,摘要 辉哥在学习蚂蚁BAAS系统时,发现了一堆证书或者公私钥名称,包括trustCa,ca.crt,client.crt,client.key,pub.txt,MyPKCS12.p12等等文件,不 ...
- SSL双向认证和SSL单向认证的流程和区别
refs: SSL双向认证和SSL单向认证的区别 https://www.jianshu.com/p/fb5fe0165ef2 图解 https 单向认证和双向认证! https://cloud.te ...
- SSL/TLS 双向认证(一) -- SSL/TLS工作原理
本文部分参考: https://www.wosign.com/faq/faq2016-0309-03.htm https://www.wosign.com/faq/faq2016-0309-04.ht ...
- SSL/TLS工作原理
一: SSL/TLS介绍 什么是SSL,什么是TLS呢?官话说SSL是安全套接层(secure sockets layer),TLS是SSL的继任者,叫传输层安全(transport layer se ...
- SSL/TLS 双向认证(一) -- SSL/TLS 工作原理
本文部分参考: https://www.wosign.com/faq/faq2016-0309-03.htm https://www.wosign.com/faq/faq2016-0309-04.ht ...
最新文章
- 微信小程序lottiejs动画事例代码
- 使用触发器即时同步两个表的实例
- 三步解决C语言中struct字节对齐问题,Python进阶篇-struct字节对齐问题
- MongoDB自动备份全过程实录
- iOS 在UILabel显示不同的字体和颜色(转)
- 常见WEB漏洞描述及修复建议(可收藏写报告用)-句芒安全实验室
- Linux gcc 制作静态库01
- Java笔记-多线程相关
- 兄弟们,请求支援,怎么实现互通,全部都互通的
- java中的triple_无法在使用Java加密的.NET中使用TripleDES进行解密
- Angr安装与使用之使用篇(三)
- 搭建自己的框架WedeNet(五)
- Jquery监听input回车事件
- 手机用计算机微信运动记步吗,微信运动怎么用?微信运动计步功能使用方法介绍...
- 不得不说,这19个程序员兼职平台让我1年收入60w
- 关于 BBBB 站视频删除问题解释
- FTP服务器有时候连接超时的问题
- 大坝平台结构——志豪未来科技有限公司
- 7大车企电动策略,平台化到底是解药还是毒药?
- 域名升级访问中拿笔记好_赶快看看你的电脑中是否被植入了这个软件
热门文章
- 发票查重触手可得,会计从此不背锅
- 利用二维数组实现一个矩阵类:Matrix
- 深度学习:GPU云服务器是如何租用的
- 论一次feign微服务直接多文件上传由版本引发的血案
- 实体之间的关系主要有以下两种
- tp5.1 乐视云上传视频文件(https请求http乐视云上传接口)http网址下上传视频(https API接口)
- mysql 表的结果作为表_SQL--如何将Sql语句查询出来的结果作为一个表名 再次进行查询...
- 【博学谷学习记录】超强总结,用心分享|人工智能第一课Python的认识与环境搭建。
- 博学谷 java_博学谷javaee在线就业班2020
- 1.10HDFS 回收站机制