文章目录

前言

一、需求概述

二、总体设计

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和证书)相关推荐

  1. MacOS下使用C语言基于openssl库进行RSA加密解密

    MacOS下使用C语言基于openssl库进行RSA加密解密 1 安装openssl并生成密钥 首先当然要安装openssl(这里记得看一下安装路径,应该是/usr/local/Cellar/open ...

  2. Linux下C语言使用openssl库进行MD5校验

    http://blog.csdn.net/cassie_huang/article/details/53212933 作者:无脑仔的小明  出处:http://www.cnblogs.com/wuna ...

  3. 大文件MD5计算 C语言 (从OpenSSL库中分离算法:三)

    从OpenSSL库中分离算法-MD5算法-大文件MD5计算 续上述博客 小文件计算MD5时,可以把文件数据一次性都读到内存中计算,但当文件很大时,将文件一次性读到内存中是不可行的,此时,需要对文件数据 ...

  4. 使用Python Openssl库解析X509证书信息

    文章目录 X.509 证书结构描述 证书数据结构 源码 编译运行输出结果 参考文献 X.509 证书结构描述 常见的X.509证书格式包括: 对于常见的https证书 一般是用crt或者pem来保存, ...

  5. C语言使用openssl库进行加密

    概述 在密码学里面一共有3中分类: 1.对称加密/解密 对称加密比较常见的有DES/AES.加密方和解密方都持有相同的密钥.对称的意思就是加密和解密都是用相同的密钥. 2.非对称加密/解密 常见的加密 ...

  6. 单片机c语言 openssl,Linux下C语言使用openssl库进行加密

    在这里插一小节加密的吧,使用openssl库进行加密. 使用MD5加密 我们以一个字符串为例,新建一个文件filename.txt,在文件内写入hello ,然后在Linux下可以使用命令md5sum ...

  7. c语言使用openssl库进行RSA加解密,并使用OAEP SHA256填充方式

    参考链接: https://blog.csdn.net/github_35454460/article/details/51862470 https://developer.aliyun.com/ar ...

  8. C语言利用openSSL库AES模块加密

    概述 在密码学里面一共有3中分类: 1.对称加密/解密 对称加密比较常见的有DES/AES.加密方和解密方都持有相同的密钥.对称的意思就是加密和解密都是用相同的密钥. 2.非对称加密/解密 常见的加密 ...

  9. 二级c语言上机题库及解析,2013年计算机二级C语言上机题库及答案解析(3)

    填空题 给定程序中,函数fun的功能是:在形参ss所指字符串数组中,查找含有形参substr所指子串的所有字符串并输出,若没找到则输出相应信息.ss所指字符串数组中共有N个字符串,且串长小于M.程序中 ...

  10. c-ares 一个C语言的异步DNS解析库

    c-ares是一个C语言的异步DNS解析库,可以很方便的和使用者的事件循环统一起来,实现DNS的非 阻塞异步解析,libcurl, libevent, gevent, nodejs都在使用. 下面摘自 ...

最新文章

  1. ARTS打卡计划第一周-Share-系统字典模块的设计
  2. HDU-2732 Leapin' Lizards 最大流
  3. 网络营销外包期间如何提升网络营销外包外链优化效果?
  4. 最小生成树的两个性质:
  5. centos7之破解root密码
  6. Docker-compose配置Mysql,Redis,MongoDB
  7. Arch Linux 安装总结
  8. 哈夫曼树(利用python实现)
  9. python市场需求如何_2020年Python市场前景广阔
  10. 从单机到集群会话的管理之集群模式一
  11. 一种机器人语音识别系统及其工作方法与流程
  12. paip.mysql 批量kill 连接.
  13. 做饭给自己一人吃,如何最快速,且营养有保证?
  14. 3d打印英语文献_3D打印合集,从设计,技术到工业制造应用!
  15. centos7搭建ftp服务
  16. 软件著作权转让的收费标准怎样
  17. 微信控制树莓派运行python_树莓派笔记07-微信公众号控制树莓派(一)
  18. 刘东明应邀赴台湾担任金手指网络奖终审评委
  19. 电脑PDF阅读器哪个好用?这三个阅读器值得收藏
  20. 从新手到Flutter架构师,一篇就够!深度解析,值得收藏

热门文章

  1. 《指弹:HARD RAIN》
  2. 干货 | 4步带你完成私有云盘搭建
  3. 音声合成:音高、泛音、谐波、基频 到底是什么概念?
  4. flutter 真机无法调试 sdk报错_中小团队的Flutter实践经验总结
  5. 三菱plc传送文件到服务器,三菱Q系列PLC通过FTP文件传输案例介绍
  6. [ZROI1788]计算器
  7. 【矩阵论】线性空间与线性变换(1)
  8. 使用 AES 对称加密算法对视频文件进行加密解密(C++ 及 Java 实现)
  9. 2021级新生个人训练赛第37场
  10. php源码比赛,TSRC挑战赛: PHP防御绕过挑战实录