2.进入正题

上篇文章介绍了如何简单搭建一个环境帮助我们分析,今天我们就进入正题,开始在这个环境下分析。

我们使用IE浏览器访问Web服务器根目录的test.txt文件并抓包,可以抓到如下6个包(前面的TCP三次握手在此略过):

使用服务器私钥解密后的包是这个样子的:

接下来我们就结合这6个包来分析一下一个完整的HTTPS加解密流程。

第1包

Client Hello是TLS握手的第一步,客户端会将一个随机数、支持的加密套件、压缩算法等信息发送给服务器。

第2包

  1. Server Hello,用来响应客户端的Client Hello,里面同样包含一个32字节的随机数,以及服务端选择的加密套件和压缩算法。
  2. Certificate,服务端会把自己的证书发送给客户端,用来证明自己的身份,证书里面包含一个公钥,供后面的密钥交换使用。(客户端也可以发送证书证明身份,但是比较少见,我们这里就没有客户端证书)。
  3. Server Hello Done,用以表示服务端的密钥交换过程已经结束。

第3包

  1. Client Key Exchange,包含一个使用服务器公钥加密的预主密钥PreMasterSecret,解密后可以用来生成密钥。
  2. Change Cipher Spec,表明握手协议已经完成。
  3. Finished,表示握手结束,这条消息已经被协商好的密钥加密,也可以起到确认密钥的作用。

解密后的Finish消息如下,这里面包含一个Verify Data,是利用PRF函数算出来的,这个函数接下来会介绍,这里我们只需要知道函数的输入参数有:(1)两个hash值,是之前所有握手消息的MD5和SHA1;(2)MasterSecret,由PreMasterSecret生成;(3)finished_label,服务端使用“server finished”,客户端使用“client finished”。

接下来重点介绍下密钥的生成(下面代码中的加解密函数使用了OpenSSL库):

解密Encrypted PreMasterSecret

刚才说到PreMasterSecret被服务器的公钥加密了,所以需要使用服务器的私钥解密,直接上代码:

FILE * priv_fp = fopen("C:\\Users\\hello\\Desktop\\server.key","r");//server.key为之前生成的服务器私钥文件
if (priv_fp == NULL)
{
printf("read key error\n");
return -1;
} RSA *rsa = PEM_read_RSAPrivateKey(priv_fp, NULL, NULL, NULL);
if (rsa == NULL)
{
printf("read key error\n");
return -1;
} len = RSA_private_decrypt(128, encrypted_premaster, premaster, rsa, RSA_PKCS1_PADDING);

可以解密出来48字节的PreMasterSecret:

密钥生成

密钥生成要使用一个很重要的伪随机函数,Pseudo-random Fuction(PRF),PRF函数原理如下:

该函数有3个输入,其中Secret相当于密钥;Label是一个标识符,不同场合会使用不同的字符串,比如“server finished”、"master secret"等;Seed是一个种子值,比如客户端和服务器的随机数。

该函数的代码实现如下:

static int tls_prf(Data *secret,char *usage,Data *rnd1,Data *rnd2,Data *out)
{
int r,_status;
Data *md5_out=0,*sha_out=0;
Data *seed;
UCHAR *ptr;
Data *S1=0,*S2=0;
int i,S_l; if(r=r_data_alloc(&md5_out,MAX(out->len,16)))
return -1;
if(r=r_data_alloc(&sha_out,MAX(out->len,20)))
return -1;
if(r=r_data_alloc(&seed,strlen(usage)+rnd1->len+rnd2->len))
return -1;
ptr=seed->data;
memcpy(ptr,usage,strlen(usage)); ptr+=strlen(usage);
memcpy(ptr,rnd1->data,rnd1->len); ptr+=rnd1->len;
memcpy(ptr,rnd2->data,rnd2->len); ptr+=rnd2->len; S_l=secret->len/2 + secret->len%2; if(r=r_data_alloc(&S1,S_l))
return -1;
if(r=r_data_alloc(&S2,S_l))
return -1; memcpy(S1->data,secret->data,S_l);
memcpy(S2->data,secret->data + (secret->len - S_l),S_l); if(r=tls_P_hash
(S1,seed,EVP_get_digestbyname("MD5"),md5_out))
return -1;
if(r=tls_P_hash(S2,seed,EVP_get_digestbyname("SHA1"),sha_out))
return -1; for(i=0;i<out->len;i++)
out->data[i]=md5_out->data[i] ^ sha_out->data[i]; _status=0;
abort:
r_data_destroy(&md5_out);
r_data_destroy(&sha_out);
r_data_destroy(&seed);
r_data_destroy(&S1);
r_data_destroy(&S2);
return(_status);
}

PRF要使用一个扩展函数(P_hash),原理图如下:

该函数的代码实现如下:

static int tls_P_hash(Data *secret,Data *seed,const EVP_MD *md,Data *out)
{
UCHAR *ptr=out->data;
int left=out->len;
int tocpy;
UCHAR *A;
UCHAR _A[20],tmp[20];
unsigned int A_l,tmp_l;
HMAC_CTX hm; A=seed->data;
A_l=seed->len; while(left){
HMAC_Init(&hm,secret->data,secret->len,md);
HMAC_Update(&hm,A,A_l);
HMAC_Final(&hm,_A,&A_l);
A=_A; HMAC_Init(&hm,secret->data,secret->len,md);
HMAC_Update(&hm,A,A_l);
HMAC_Update(&hm,seed->data,seed->len);
HMAC_Final(&hm,tmp,&tmp_l); tocpy=MIN(left,tmp_l);
memcpy(ptr,tmp,tocpy);
ptr+=tocpy;
left-=tocpy;
} HMAC_cleanup(&hm);
return 0;
}

了解了PRF函数后,就可以使用它做密钥生成(密钥扩展)了,下图完整阐述了密钥生成过程:

密钥生成代码如下:

tls_prf(&pre_master_secret, "master secret", &random1, &random2, &master_secret);

tls_prf(&master_secret, "key expansion", &random2, &random1, &key_block);

for (int i=0; i<16; i++)
{
client_write_key[i] = key_block.data[40+i];
}

第一次调用PRF函数,使用PreMasterSecret、"master secret"和两个随机数(上述服务器和客户端各一个)作为输入参数,输出为一个48字节的主密钥MasterSecret:

第二次调用PRF函数,MasterSecret、"key expansion"和两个随机数作为输入参数,输出为一个Key_block,从41字节开始的16个字节为Client Write key,接下来16个字节为Server Write key,这两个就是接下来双方通信使用的RC4密钥:

第4包

  1. Change Cipher Spec,表明握手协议已经完成。
  2. Finished,表示握手结束,这条消息已经被协商好的密钥加密。

第5、6包

接下来就是传输应用层的信息了,这些信息使用之前协商好的密钥(Client Write key、Server Write key)加密,以客户端为例,解密代码如下:

EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int rv, outl;
rv = EVP_DecryptInit_ex(&ctx, EVP_rc4(), NULL, client_write_key, iv);//初始向量IV为0
EVP_DecryptUpdate(&ctx, out, &outl, ciphertext, ciphertextlen);

解密后的最后20个字节为MAC校验,这里使用的是SHA1算法。

解密后的客户端数据:

同理,解密后的服务端数据:

至此,一个完整的HTTPS加解密流程就结束了,过程还是比较简单,只是如果自己实现的话一些细节会比较让人头疼,给出代码可以少走一些弯路,至于更复杂的加密套件,这里就不再介绍,流程应该差不太多,有兴趣的朋友可以研究一下。

参考:http://www.360doc.com/content/16/0320/21/30136251_543905971.shtml

HTTPS加密流程超详解(二)的更多相关文章

  1. HTTPS加密流程超详解(一)前期准备

    0.前言 前一阵子想写一个HTTPS的嗅探工具,之前只是大致了解SSL/TLS协议的加密流程,真正上起手来一步一步分析发现还是有点复杂的,于是我参考了wireshark的源码以及各种RFC,弄清楚了S ...

  2. https加密解密过程详解

    要点: https协议对传输内容进行加密,具有更强的安全性,防止被抓包后解析出请求内容. https是建立在ssl之上的http协议. 服务器支持https协议必须安装一套数字证书,所谓数字证书就是一 ...

  3. HTTPS详解二:SSL / TLS 工作原理和详细握手过程

    HTTPS 详解一:附带最精美详尽的 HTTPS 原理图 HTTPS详解二:SSL / TLS 工作原理和详细握手过程 在上篇文章HTTPS详解一中,我已经为大家介绍了 HTTPS 的详细原理和通信流 ...

  4. 阿里云slb和ucloud负载均衡ulb添加ssl证书将http服务https化的配置详解

    阿里云和ucloud服务器配置ssl证书将http服务https化的配置详解 项目背景: 苹果App于2017年1月1日将启用App Transport Security安全功能,即强制App通过HT ...

  5. html5的float属性超详解(display,position, float)(文本流)

    html5的float属性超详解(display,position, float)(文本流) 一.总结 1.文本流: 2.float和绝对定位都不占文本流的位置 3.普通流是默认定位方式,就是依次按照 ...

  6. Mysql超详解

    Mysql超详解 一.命令框基本操作及连接Mysql 找到Mysql安装路径,查看版本 同时按快捷键win+R会弹出一个框,在框中输入cmd 点击确定后会出现一个黑框,这是命令框,我们的操作要在这命令 ...

  7. HTTPS加密流程理解

    HTTPS加密流程 由于HTTP的内容在网络上实际是明文传输,并且也没有身份验证之类的安全措施,所以容易遭到挟持与攻击 HTTPS是通过SSL(安全套接层)和TLS(安全传输协议)的组合使用,加密TC ...

  8. pika详解(二) BlockingConnection

    pika详解(二) BlockingConnection   本文链接:https://blog.csdn.net/comprel/article/details/94592348 版权 Blocki ...

  9. Shiro 安全框架详解二(概念+权限案例实现)

    Shiro 安全框架详解二 总结内容 一.登录认证 二.Shiro 授权 1. 概念 2. 授权流程图 三.基于 ini 的授权认证案例实现 1. 实现原理图 2. 实现代码 2.1 添加 maven ...

随机推荐

  1. Django 2.0 新特性 抢先看!

    一.Python兼容性 Django 2.0支持Python3.4.3.5和3.6.Django官方强烈推荐每个系列的最新版本. 最重要的是Django 2.0不再支持Python2! Django ...

  2. 数据库服务器---Qps

    QPS(Query Per Second)意思为"每秒查询率",是一台服务器每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准.同时也代表一种计算 ...

  3. nignx 测试配置文件

    nginx -t nginx: the configuration file /usr/local/nginx-1.2.9/conf/nginx.conf syntax is ok nginx: co ...

  4. github与git之间怎么建立连接

    git是一款强大的分布式版本控制系统,到底是什么意思呢?其实我也不知道,以后知道了再补上吧.下面咱们来讲一下github怎么与你电脑上的git建立起连接关系. 一.你要有一个github账号. 去百度 ...

  5. 高质量PHP代码的50个实用技巧必备(下)

    26. 避免直接写SQL, 抽象之 不厌其烦的写了太多如下的语句: ? 1 2 <span style="color:#333333;font-family:''Helvetica, ...

  6. python处理csv文件问题解决贴

    实际工作中,碰到这么个问题:有个软件跑在linux系统上,其中用到一个数据库是csv格式的,但要向这个数据库添加600行新的数据,数据源同样是一个csv格式的文件. 有了目标,开始干活.首先想到的是, ...

  7. linux下的数据库管理工具phpmyadmin安装以及文件大小限制的配置修改

    1.首先需要安装mysql和apache服务.具体安装过程百度; 2.安装php环境以及对apache的扩展; sudo apt install php7.0  对于这些软件可能还需要各自进行配置,这 ...

  8. 班级博客客户端Beta阶段发布说明

    班级博客客户端Beta阶段发布说明 NewTeam 2017/12/18 项目 博客园班级博客Android客户端 目录 发布方式和发布地址 新功能 修复的缺陷 对运行环境的要求 安装方法 已知的问题 ...

  9. SAP GUI 750 安装包 及 补丁3 共享

    SAP GUI 750 安装包 及 补丁3 共享 链接: https://pan.baidu.com/s/1hstkfUs%20 密码: ggbz -------------------------- ...

  10. 读取指定文件夹下的全部文件,可通过正则进行过滤,返回文件路径数组 -- 基于node的一个函数

    var fs = require('fs'); // 模板文件夹路径 var templateDirectory = '../src'; //相对于当前文件的相对路径 //var templateDi ...