C语言使用openssl库解析TLS报文(SNI和证书)
文章目录
前言
一、需求概述
二、总体设计
1.SNI字段的过滤
2.证书的解析
总结
2023.1.11更新:
首先痛批各大网站的爬虫,直接把这篇文章拉到自己的平台中,而且还要收费???(某库)
根据这段时间的积累,我还是发现openssl官方代码中还是涉及到SNI解析的源代码,路径为:
openssl/ssl/statem/extensions_srvr.c,其中的关键函数为tls_parse_ctos_server_name.
如下图所示:
我从代码的一点点分析中发现,其中报文解析的关键数据结构为PACKET结构体,定义在ssl/packet_locl.h头文件中,如下图所示:
从中可以发现,其中还提供了PACKET结构体操作的各种函数,而且openssl官方代码中解析报文的函数大致均出自这里!!!(tls_parse_ctos_server_name函数中所用的PACKET_get_1之类的函数均出自这里)
到这里,我就又有了新的想法:将此结构体相关的函数拷贝出来,替换之前裸的写法,解析起来不就更安全、更不容易出错了嘛!我们可以看到,此头文件依赖于internal/numbers.h头文件,我们同样将其拷贝到项目中进行使用,同时,numbers.h又不依赖于其他的openssl内部头文件,那我们的项目结构(GitHub - CChuancey/Protocol_Restoration: tcp流的乱序重组、http与tls应用层)变化如下:
同时,CMakeLists.txt中添加相关依赖就可以直接使用了!
最后,我已将基于Openssl库中PACKET结构体解析SNI的代码提交至Github仓库(具体位置为src/tls.cpp中的get_server_extension_name_by_openssl函数),欢迎Star!
GitHub - CChuancey/Protocol_Restoration: tcp流的乱序重组、http与tls应用层
说明:以上代码来自官方openssl 1.1.1s源代码,感谢FuXiao在Github仓库中提出的Issues!
前言
项目中需要对TLS报文中SNI和证书部分进行过滤,并且之前并没有接触过这方面的内容,为了解决这一需求,期间学习了不少知识,也走了不少弯路。就写下这篇博客记录分享一下。
项目背景:项目是一个类似防火墙的软件,可以过滤特定的TLS流量。实现原理就是通过对TLS数据包流量进行解析,得到想要过滤的字段数据,根据过滤规则决定是否放行该TLS流量。
一、需求概述
本次需要过滤的TLS数据包主要针对SNI和证书,TLS报文的版本为1.2。使用版本为1.0.2k的openssl库进行C编程。
二、总体设计
1.SNI字段的过滤
sni字段存在于Client hello 报文中,在https中表示客户端浏览器对自己要访问域名的说明。openssl中获取sni字段的api也有:
const char *SSL_get_servername(const SSL *s, const int type);
但这个函数依赖于SSL连接,在项目开始探索的初期,我尝试借鉴博客旁路解密https模拟构建ssl连接的方法,发现这种方案需要修改openssl源码,而我本身对openssl库都不了解,修改源码更是难上加难,当时并没有放弃,我想着先了解openssl再去看源码,当时还发现了一本好书:《深入浅出 https从原理到实战》,如果你和我一样并不了解openssl库以及tls,可以先看这本书搭建起来知识框架,实现对openssl以及tls的一些基本了解。
看完这本书后,我尝试阅读openssl源码,发现对openssl的了解依旧不够深入,现在我已经对openssl的大致框架有了了解,想着先用好openssl再去修改源码。这一阶段我阅读了openssl库的中文api手册,对openssl有了更近一步的了解,也尝试参考openssl提供的examples写出了一对简易的server和client程序。
在阅读api和写server/client程序中,我发现一些库并不依赖于ssl连接,于是我去搜索有没有不依赖于ssl连接提取sni的api,在stack overflow上有大牛自己动手实现了一个(链接我找不到了)。代码我贴在下面:
static char* get_server_extension_name(const char* data, uint32_t datalen) {/* Skip past fixed length records:1 Handshake Type3 Length2 Version (again)32 Randomnext Session ID Length*/int pos = 38;/* session id */if (datalen < pos + 1)return NULL;uint16_t len = data[pos];pos += len + 1;/* Cipher Suites */if (datalen < pos + 2)return NULL;memcpy(&len, &data[pos], 2);len = ntohs(len);pos += len + 2;/* Compression Methods */ if (datalen < pos + 1)return NULL;len = data[pos];pos += len + 1;/* Extensions */if (datalen < pos + 2)return NULL;memcpy(&len, &data[pos], 2);len = ntohs(len); // 此时len为Extensions的长度pos += 2;// parse extensions to get sniuint16_t extension_item_len;/* Parse each 4 bytes for the extension header */while (pos + 4 <= len) {memcpy(&extension_item_len, &data[pos + 2], 2);extension_item_len = ntohs(extension_item_len);if (data[pos] == 0x00 && data[pos + 1] == 0x00) { // sni 字段if (pos + 4 + extension_item_len > len)return NULL;// get sni stringpos += 6;uint16_t server_name_len;uint16_t extension_end = pos + extension_item_len - 2;while (pos + 3 < extension_end) {memcpy(&server_name_len, &data[pos + 1], 2);server_name_len = ntohs(server_name_len);if (pos + 3 + server_name_len > extension_end)return NULL;char* hostname = (char*)malloc(server_name_len + 1);switch (data[pos]) {case 0x00: /*host name*/if (hostname == NULL) {fprintf(stderr, "malloc hostname failed!\n");return NULL;}strncpy(hostname, (char*)(data + pos + 3),server_name_len);hostname[server_name_len] = '\0';return hostname;break;default:puts("encouter error! debug me....");}pos += 3 + len;}}pos += 4 + extension_item_len;}return NULL;
}
虽然不是我自己动手写出来的sni字段提取的代码,但在此期间学习一些openssl和tls知识也算是有点收获吧。
2.证书的解析
TLS中的证书在openssl库中用X509表示,X509证书库封装了一些对证书的操作,这部分调库即可。对于证书,需要验证证书链、服务器实体证书的校验:与域名匹配、有效期等。
参考博客中内容,实现了以下函数
// 读取内存中(数组)的证书,将其解析为X509结构体
const unsigned char *data = ... ;
size_t len = ... ;X509 *cert = d2i_X509(NULL, &data, len);
if (!cert) {fprintf(stderr, "unable to parse certificate in memory\n");return EXIT_FAILURE;
}// any additional processing would go here..X509_free(cert);/*** @brief 创建一个证书链,方便后续新增需求,方便后续验证证书链** @param data* @param datalen* @return int*/
int create_stack(const unsigned char* data,uint32_t datalen,STACK_OF(X509) * sk) {uint32_t len;uint32_t pos = 0;while (pos + 3 < datalen) {ntoh(data + pos, &len);if (len == 0)return 1;const unsigned char* tmp = data + pos + 3;X509* cert = d2i_X509(NULL, &tmp, len);if (cert == NULL) {fprintf(stderr, "parse certificates failed!\n");return -1;}// verify signature,只能实现对self signature的验证EVP_PKEY* pkey = X509_get_pubkey(cert);if (pkey == NULL) {fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));return 0;}int r = X509_verify(cert, pkey);if (r <= 0) {fprintf(stderr, "certificate signature error!\n");return 0;}EVP_PKEY_free(pkey);sk_X509_push(sk, cert);pos += 3 + len;}return 1;
}/*** @brief 检查证书的有效期** @param cert* @return true* @return false*/
bool check_certificate_validity(X509* cert) {if (cert == NULL)return false;ASN1_TIME* not_before = X509_get_notBefore(cert);ASN1_TIME* not_after = X509_get_notAfter(cert);int day, sec;if (!ASN1_TIME_diff(&day, &sec, NULL, not_before)) {fprintf(stderr, "asn1 time format error!\n");return false;}if (day >= 0 || sec >= 0) {return false;}if (!ASN1_TIME_diff(&day, &sec, NULL, not_after)) {fprintf(stderr, "asn1 time format error!\n");return false;}if (day <= 0 || sec <= 0) {return false;}return true;
}/*** @brief 从证书中加载位置信息到location中** @param location* @return int*/
int get_subject_location_string(STACK_OF(X509) * sk,char* location[ENTRY_DEPTH]) {unsigned len = sk_X509_num(sk);if (len == 0) { //空的证书链return -1;}// 提取subject的信息,进行过滤X509* cert = sk_X509_value(sk, 0);X509_NAME* subj = X509_get_subject_name(cert);for (int i = 0; i < X509_NAME_entry_count(subj); i++) {X509_NAME_ENTRY* e = X509_NAME_get_entry(subj, i);ASN1_STRING* d = X509_NAME_ENTRY_get_data(e);int nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));char* value = (char*)ASN1_STRING_data(d);location[nid - NID_commonName] = new char[strlen(value) + 1];// (char*)malloc(sizeof(char) * (strlen(value) + 1));strncpy(location[nid - NID_commonName], value, strlen(value));location[nid - NID_commonName][strlen(value)] = '\0';// puts(value);}return 0;
}
总结
作为一个编程菜鸟,对于开源库的使用并没有什么经验,通过此次openssl的学习使用,我能得到如下经验教训:
对于openssl的学习过程,应该要先看书/资料对openssl本身要有一个大致的了解,然后再去学习怎么使用,最后才应该是挖掘它的实现原理以及阅读/修改源码。这样循序渐进的学习方式看似耗时实则效率最高。
最后,附上此项目的源代码:实现了从捕获流量-->tcp报文的乱序重组-->tls的解析过滤
C语言使用openssl库解析TLS报文(SNI和证书)相关推荐
- MacOS下使用C语言基于openssl库进行RSA加密解密
MacOS下使用C语言基于openssl库进行RSA加密解密 1 安装openssl并生成密钥 首先当然要安装openssl(这里记得看一下安装路径,应该是/usr/local/Cellar/open ...
- Linux下C语言使用openssl库进行MD5校验
http://blog.csdn.net/cassie_huang/article/details/53212933 作者:无脑仔的小明 出处:http://www.cnblogs.com/wuna ...
- 大文件MD5计算 C语言 (从OpenSSL库中分离算法:三)
从OpenSSL库中分离算法-MD5算法-大文件MD5计算 续上述博客 小文件计算MD5时,可以把文件数据一次性都读到内存中计算,但当文件很大时,将文件一次性读到内存中是不可行的,此时,需要对文件数据 ...
- 使用Python Openssl库解析X509证书信息
文章目录 X.509 证书结构描述 证书数据结构 源码 编译运行输出结果 参考文献 X.509 证书结构描述 常见的X.509证书格式包括: 对于常见的https证书 一般是用crt或者pem来保存, ...
- C语言使用openssl库进行加密
概述 在密码学里面一共有3中分类: 1.对称加密/解密 对称加密比较常见的有DES/AES.加密方和解密方都持有相同的密钥.对称的意思就是加密和解密都是用相同的密钥. 2.非对称加密/解密 常见的加密 ...
- 单片机c语言 openssl,Linux下C语言使用openssl库进行加密
在这里插一小节加密的吧,使用openssl库进行加密. 使用MD5加密 我们以一个字符串为例,新建一个文件filename.txt,在文件内写入hello ,然后在Linux下可以使用命令md5sum ...
- c语言使用openssl库进行RSA加解密,并使用OAEP SHA256填充方式
参考链接: https://blog.csdn.net/github_35454460/article/details/51862470 https://developer.aliyun.com/ar ...
- C语言利用openSSL库AES模块加密
概述 在密码学里面一共有3中分类: 1.对称加密/解密 对称加密比较常见的有DES/AES.加密方和解密方都持有相同的密钥.对称的意思就是加密和解密都是用相同的密钥. 2.非对称加密/解密 常见的加密 ...
- 二级c语言上机题库及解析,2013年计算机二级C语言上机题库及答案解析(3)
填空题 给定程序中,函数fun的功能是:在形参ss所指字符串数组中,查找含有形参substr所指子串的所有字符串并输出,若没找到则输出相应信息.ss所指字符串数组中共有N个字符串,且串长小于M.程序中 ...
- c-ares 一个C语言的异步DNS解析库
c-ares是一个C语言的异步DNS解析库,可以很方便的和使用者的事件循环统一起来,实现DNS的非 阻塞异步解析,libcurl, libevent, gevent, nodejs都在使用. 下面摘自 ...
最新文章
- ARTS打卡计划第一周-Share-系统字典模块的设计
- HDU-2732 Leapin' Lizards 最大流
- 网络营销外包期间如何提升网络营销外包外链优化效果?
- 最小生成树的两个性质:
- centos7之破解root密码
- Docker-compose配置Mysql,Redis,MongoDB
- Arch Linux 安装总结
- 哈夫曼树(利用python实现)
- python市场需求如何_2020年Python市场前景广阔
- 从单机到集群会话的管理之集群模式一
- 一种机器人语音识别系统及其工作方法与流程
- paip.mysql 批量kill 连接.
- 做饭给自己一人吃,如何最快速,且营养有保证?
- 3d打印英语文献_3D打印合集,从设计,技术到工业制造应用!
- centos7搭建ftp服务
- 软件著作权转让的收费标准怎样
- 微信控制树莓派运行python_树莓派笔记07-微信公众号控制树莓派(一)
- 刘东明应邀赴台湾担任金手指网络奖终审评委
- 电脑PDF阅读器哪个好用?这三个阅读器值得收藏
- 从新手到Flutter架构师,一篇就够!深度解析,值得收藏
热门文章
- 《指弹:HARD RAIN》
- 干货 | 4步带你完成私有云盘搭建
- 音声合成:音高、泛音、谐波、基频 到底是什么概念?
- flutter 真机无法调试 sdk报错_中小团队的Flutter实践经验总结
- 三菱plc传送文件到服务器,三菱Q系列PLC通过FTP文件传输案例介绍
- [ZROI1788]计算器
- 【矩阵论】线性空间与线性变换(1)
- 使用 AES 对称加密算法对视频文件进行加密解密(C++ 及 Java 实现)
- 2021级新生个人训练赛第37场
- php源码比赛,TSRC挑战赛: PHP防御绕过挑战实录