SSL / TLS协议解析!SNI 识别
作者:互联网
自Web诞生以来,我们所接触的互联网时代,都有可能存在信息的截断,而SSL协议及其后代TLS提供了加密和安全性,使现代互联网安全成为可能。
这些协议已有将近二十多年的历史,其特点是不断更新,旨在与日趋复杂的攻击者保持同步。
什么是SSL!什么是TLS!
SSL代表安全套接字层,该协议是由Netscape于1990年代中期开发的,Netscape是当时最受欢迎的Web浏览器。SSL 1.0从未向公众发布,而SSL 2.0具有严重的缺陷。1996年发布的SSL 3.0进行了彻底的改进,为后续工作奠定了基础。
而TLS与SSL,当一版本于1999年发布时,它由Internet工程任务组(IETF)进行了标准化,并被赋予了一个新名称:传输层安全性( TLS)。
正如TLS规范指出的那样,“此协议与SSL 3.0的区别并不明显。” 因此,TLS和SSL并不是真正的问题。而是,这两者形成了一系列不断更新的协议,并且经常被合并为SSL / TLS。
这样我们就明白了TLS(传输层安全性)只是SSL的更新,更安全的版本。目前已经升级到TLS1.3版本,TLS协议对所有类型的Internet通信进行加密。最常见的是网络流量;
简而言之,它们是保持Internet连接安全并保护两个系统之间发送的任何敏感数据的标准技术,可防止犯罪分子读取和修改所传输的任何信息,包括潜在的个人详细信息。
SSL/TLS握手过程
握手过程非常复杂,并且协议允许有多种变体。以下步骤提供了一个大致的概述。
第一
- 客户端与服务器联系并请求安全连接。服务器以密码套件列表(即创建加密连接的算法工具包)答复
- 客户端将其与其自己的受支持密码套件列表进行比较,选择一个,然后让服务器知道它们都将使用它。
第二
- 服务器提供其数字证书,该数字证书是由第三方机构颁发的确认服务器身份的电子文档。包含服务器的公共加密密钥等等。
- 客户端收到证书后,便会确认证书的真实性。
第三
-
客户端和服务器使用服务器的公共密钥,建立会话密钥,这两个会话密钥将用于会话的其余部分以加密通信。有几种技术可以做到这一点。
-
客户端可以使用公共密钥对随机数进行加密,然后将其发送到服务器进行解密,然后双方都可以使用该数字来建立会话密钥。
-
或者,两方可以使用所谓的Diffie-Hellman密钥交换来建立会话密钥。
TLS1.2和TLS1.3比较
TLS1.2它也允许使用较旧的加密技术,以支持较旧的计算机。不幸的是,这使它容易受到攻击,在中间人攻击中,黑客拦截了通信中的数据包,并在读取或更改数据包后将其发送出去。
幸运的是,TLS1.3通过抛弃对旧加密系统的支持,填补了许多此类漏洞。使通信中的数据包受到了保护。
什么是SNI
Server Name Indication (SNI) 是TLS协议(以前称为SSL协议)的扩展,该协议在HTTPS中使用。它包含在TLS/SSL握手流程中,以确保客户端设备能够看到他们尝试访问的网站的正确SSL证书。该扩展使得可以在TLS握手期间指定网站的主机名或域名 ,而不是在握手之后打开HTTP连接时指定。
SNI的技术原理
SNI通过让客户端发送虚拟域的名称作为TLS协商的ClientHello消息的一部分来解决此问题。这使服务器可以及早选择正确的虚拟域,并向浏览器提供包含正确名称的证书。
这样要说一下,服务器名称指示(SNI)有效负载未加密,因此客户端尝试连接的服务器的主机名对于被动的窃听者是可见的。
TLS的SNI扩展有什么作用?
- Web服务器通常负责多个主机名–或域名。如果网站使用HTTPS 则每个主机名将具有其自己的SSL证书。
- 在HTTPS中,先有TLS握手,然后才能开始HTTP对话。如果没有SNI,客户端将无法向服务器指示正在与之通信的主机名。
- 如果服务器可能为错误的主机名生成SSL证书。那么SSL证书上的名称与客户端尝试访问的名称不匹配,则客户端浏览器将返回错误信息,并通常会终止连接。
- 通过 SNI,拥有多虚拟机主机和多域名的服务器就可以正常建立 TLS 连接了。
下面,我们就开始对TLS协议的SNI进行解析。
TLS协议的SNI识别
这里给出的只是一部分代码实现。
static bool is_sslv3_or_tls(u_char *tls_data,int PayloadLen,int offset)
{
uint8_t content_type = 0;
uint16_t protocol_version = 0, record_length = 0;
SslSession *session;
if (PayloadLen < 5) {
return false;
}
content_type = tls_data[offset];
offset += 1;
protocol_version = ntohs(*(uint16_t*)(tls_data + offset));
offset += 2;
record_length = ntohs(*(uint16_t*)(tls_data + offset));
//printf("record_length: %d\n",record_length);
/* These are the common types. */
if (content_type != SSL_ID_HANDSHAKE) {
return false;
}
if (protocol_version != SSLV3_VERSION &&
protocol_version != TLSV1_VERSION &&
protocol_version != TLSV1DOT1_VERSION &&
protocol_version != TLSV1DOT2_VERSION) {
return false;
}
if (record_length == 0 || record_length >= TLS_MAX_RECORD_LENGTH + 2048) {
return false;
}
return true;
}
static void dissect_ssl2_hnd_client_hello(u_char *tls_data,int PayloadLen, int offset)
{
uint32_t offset_end = 0;
uint16_t ext_type = 0;
uint32_t ext_len = 0;
uint32_t next_offset = 0;
int msg_length = 0;
int remaining_length = 0;
uint32_t server_name_length = 0;
uint16_t length = 0;
uint16_t version = 0;
int session_id_len = 0;
uint32_t extensions_length = 0;
uint8_t random[32];
char s_name[256] = {0};
int i = 0;
uint32_t cipher_suite_length = 0;
offset += 3;/*Length*/
version = ntohs(*(uint16_t*)(tls_data + offset));
printf("Version: 0x%.2X\n",version);
offset += 2; /*Version*/
if (offset > PayloadLen)
return;
memcpy(random,tls_data + offset,32);
for (i = 0; i < 32;i++)
printf("%x",random[i]);
printf("\n");
offset += 32; /*random*/
session_id_len = tls_data[offset];
printf("session_id_len: %d\n",session_id_len);
offset += 1; /*Session ID Length*/
if (session_id_len > PayloadLen)
return;
uint8_t session_id[session_id_len];
memcpy(session_id,tls_data + offset,session_id_len);
for (i = 0; i < session_id_len;i++)
printf("%x",session_id[i]);
printf("\n");
offset += session_id_len; /*Session ID */
cipher_suite_length = ntohs(*(uint16_t*)(tls_data + offset));
printf("cipher_suite_length: %d\n",cipher_suite_length);
offset += 2;/*cipher_suite_length*/
offset += cipher_suite_length; /*Session ID */
offset += 1;/*compression_method length*/
offset += 1;/*compression_method */
extensions_length = ntohs(*(uint16_t*)(tls_data + offset));
printf("Extensions Length: %d\n",extensions_length);
printf("Extensions Length: 0x%.2X\n",extensions_length);
offset += 2;
//offset_end = PayloadLen - offset;
//printf("offset_end: %d\n",offset_end);
msg_length = PayloadLen;
while (offset < msg_length)
{
/* Get the type and length */
remaining_length = msg_length - offset;
if (remaining_length < 3) {
printf("Not enough data left for IE and length, %i bytes\n", remaining_length);
return;
}
ext_type = ntohs(*(uint16_t*)(tls_data + offset));
//printf("ext_type: 0x%.2X\n",ext_type);
offset += 2;
ext_len = ntohs(*(uint16_t*)(tls_data + offset));
//printf("ext_len: %d\n",ext_len);
offset += 2;
switch (ext_type)
{
case SSL_HND_HELLO_EXT_SERVER_NAME:
printf("==========SERVER_NAME======\n");
offset += 2; //Server Name list length
offset += 1; //Server Name Type
server_name_length = ntohs(*(uint16_t*)(tls_data + offset));
printf("server_name_length: %d\n",server_name_length);
offset += 2; //Server Name length
memset(s_name,0,256);
memcpy(s_name,tls_data + offset,server_name_length);
printf("Server Name: %s\n",s_name);
break;
default:
break;
}
offset += ext_len;
}
}
void dissect_tls(u_char *tls_data,int PayloadLen,int offset)
{
SslSession *session;
uint16_t version = 0;
uint16_t length = 0;
uint8_t msg_type = 0;
printf("0x%.2X,0x%.2X,0x%.2X 0x%.2X,0x%.2X,0x%.2X \n",tls_data[offset],tls_data[offset+1],tls_data[offset+2],tls_data[offset+3],tls_data[offset+4],tls_data[offset+5]);
if (!is_sslv3_or_tls(tls_data,PayloadLen,offset)) {
return;
}
printf("====tls=========\n");
offset += 1; /*Content Type*/
version = ntohs(*(uint16_t*)(tls_data + offset));
printf("Version: 0x%.2X\n",version);
offset += 2; /*Version*/
length = ntohs(*(uint16_t*)(tls_data + offset));
printf("Length: %d\n",length);
offset += 2; /*length*/
/* fetch the msg_type */
msg_type = tls_data[offset];
offset += 1; /*handshake type*/
printf("handshake type: 0x%.2X\n",msg_type);
switch (version)
{
case SSLV3_VERSION:
case TLSV1_VERSION:
case TLSV1DOT1_VERSION:
case TLSV1DOT2_VERSION:
if (PayloadLen < 5)
{
break;
}
ssl_looks_like_valid_v2_handshake(tls_data,PayloadLen,msg_type,offset);
break;
default:
if (PayloadLen < 5)
{
break;
}
}
}
编译运行:
在客户端到服务端方向的 ClientHello 包中,提取 SNI 扩展字段中的 SNI_NAME 字段,通过数据包偏移量进行逐步匹配得到 SNI_NAME。对 SNI_NAME 进行规则匹配。
总结
这篇文章只是对SSL/TLS做一些简单的介绍,想通过更多认识,可以阅读官方的RFC文档,对于SNI(Server Name Indication)是 TLS 的扩展,它主要是用来解决一个服务器拥有多个域名的情况。通过 SNI,拥有多虚拟机主机和多域名的服务器就可以正常建立 TLS 连接了。
欢迎关注微信公众号【程序猿编码】,欢迎添加本人微信号(17865354792),交个朋友,咱们一起学习进步!
标签:TLS,tls,printf,SNI,SSL,length,offset,data 来源: https://blog.csdn.net/chen1415886044/article/details/116330304