HTTPS加密流程超详解(二)
2.进入正题
上篇文章介绍了如何简单搭建一个环境帮助我们分析,今天我们就进入正题,开始在这个环境下分析。
我们使用IE浏览器访问Web服务器根目录的test.txt文件并抓包,可以抓到如下6个包(前面的TCP三次握手在此略过):
使用服务器私钥解密后的包是这个样子的:
接下来我们就结合这6个包来分析一下一个完整的HTTPS加解密流程。
第1包
Client Hello是TLS握手的第一步,客户端会将一个随机数、支持的加密套件、压缩算法等信息发送给服务器。
第2包
- Server Hello,用来响应客户端的Client Hello,里面同样包含一个32字节的随机数,以及服务端选择的加密套件和压缩算法。
- Certificate,服务端会把自己的证书发送给客户端,用来证明自己的身份,证书里面包含一个公钥,供后面的密钥交换使用。(客户端也可以发送证书证明身份,但是比较少见,我们这里就没有客户端证书)。
- Server Hello Done,用以表示服务端的密钥交换过程已经结束。
第3包
- Client Key Exchange,包含一个使用服务器公钥加密的预主密钥PreMasterSecret,解密后可以用来生成密钥。
- Change Cipher Spec,表明握手协议已经完成。
- 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包
- Change Cipher Spec,表明握手协议已经完成。
- 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加密流程超详解(二)的更多相关文章
- HTTPS加密流程超详解(一)前期准备
0.前言 前一阵子想写一个HTTPS的嗅探工具,之前只是大致了解SSL/TLS协议的加密流程,真正上起手来一步一步分析发现还是有点复杂的,于是我参考了wireshark的源码以及各种RFC,弄清楚了S ...
- https加密解密过程详解
要点: https协议对传输内容进行加密,具有更强的安全性,防止被抓包后解析出请求内容. https是建立在ssl之上的http协议. 服务器支持https协议必须安装一套数字证书,所谓数字证书就是一 ...
- HTTPS详解二:SSL / TLS 工作原理和详细握手过程
HTTPS 详解一:附带最精美详尽的 HTTPS 原理图 HTTPS详解二:SSL / TLS 工作原理和详细握手过程 在上篇文章HTTPS详解一中,我已经为大家介绍了 HTTPS 的详细原理和通信流 ...
- 阿里云slb和ucloud负载均衡ulb添加ssl证书将http服务https化的配置详解
阿里云和ucloud服务器配置ssl证书将http服务https化的配置详解 项目背景: 苹果App于2017年1月1日将启用App Transport Security安全功能,即强制App通过HT ...
- html5的float属性超详解(display,position, float)(文本流)
html5的float属性超详解(display,position, float)(文本流) 一.总结 1.文本流: 2.float和绝对定位都不占文本流的位置 3.普通流是默认定位方式,就是依次按照 ...
- Mysql超详解
Mysql超详解 一.命令框基本操作及连接Mysql 找到Mysql安装路径,查看版本 同时按快捷键win+R会弹出一个框,在框中输入cmd 点击确定后会出现一个黑框,这是命令框,我们的操作要在这命令 ...
- HTTPS加密流程理解
HTTPS加密流程 由于HTTP的内容在网络上实际是明文传输,并且也没有身份验证之类的安全措施,所以容易遭到挟持与攻击 HTTPS是通过SSL(安全套接层)和TLS(安全传输协议)的组合使用,加密TC ...
- pika详解(二) BlockingConnection
pika详解(二) BlockingConnection 本文链接:https://blog.csdn.net/comprel/article/details/94592348 版权 Blocki ...
- Shiro 安全框架详解二(概念+权限案例实现)
Shiro 安全框架详解二 总结内容 一.登录认证 二.Shiro 授权 1. 概念 2. 授权流程图 三.基于 ini 的授权认证案例实现 1. 实现原理图 2. 实现代码 2.1 添加 maven ...
随机推荐
- MongoDB中聚合工具Aggregate等的介绍与使用
Aggregate是MongoDB提供的众多工具中的比较重要的一个,类似于SQL语句中的GROUP BY.聚合工具可以让开发人员直接使用MongoDB原生的命令操作数据库中的数据,并且按照要求进行聚合 ...
- Python测试开发之---string
string.letters 所有的大小写字母 >>> print string.lettersabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ ...
- Spring Boot应用 打包与部署指南
Spring Boot的打包与部署有何特点? Java Web应用在Spring Boot之前,通常是打包成war(Web application ARchive)包,结合Tomcat来完成部署. 对 ...
- 关于网页授权的两种scope的区别说明
关于网页授权的两种scope的区别说明 1.以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的.用户感知的就是直接进入了回 ...
- 魔术常量(Magic constants)
魔术常量(Magic constants) PHP中的常量大部分都是不变的,但是有8个常量会随着他们所在代码位置的变化而变化,这8个常量被称为魔术常量. __LINE__,文件中的当前行号 __FIL ...
- js(jquery)绑定点击事件
<button type="submit" id="test">test</button> 第一种 $("#test" ...
- phpcms的验证码替换 及 phpcms实现全站搜索功能
在使用phpcms替换网页的时候,除了正常的替换栏目.内容页等,其他的什么验证码啦,提交表单了,搜索功能了,这些在替换的时候可能会对一些默认文件有一些小小 的改变 下面就是自己在失败中成功的过程,最后 ...
- Xamarin 使用极光推送 详细教程
源码下载地址:http://download.csdn.net/download/kendocross/8677263 有兴趣的看以去看看 一.首先新建一个Xamarin.Android 项目,过程 ...
- Python Django CMDB项目实战之-3创建form表单,并在前端页面上展示
基于之前的项目代码 Python Django CMDB项目实战之-1如何开启一个Django-并设置base页.index页.文章页面 Python Django CMDB项目实战之-2创建APP. ...
- Ali RocketMQ与Kafka对照
淘宝内部的交易系统使用了淘宝自主研发的Notify消息中间件,使用Mysql作为消息存储媒介.可全然水平扩容,为了进一步减少成本.我们觉得存储部分能够进一步优化,2011年初,Linkin开源了Kaf ...