openssl s client客户端发送client hello,发送随机数函数是哪个

当前位置: && 知识详情
【漏洞分析】一次因漏洞修补触发的漏洞—CVE-漏洞详细分析
阅读:12761次
作者:预估稿费:500RMB(不服你也来投稿啊!)投稿方式:发送邮件至,或登陆在线投稿前言openssl发布了一个安全级别为”严重”的UAF漏洞,该漏洞利用简单,只需要发一个tcp包就能触发漏洞,但后果严重,可能导致TLS相关的应用拒绝服务,甚至任意代码执行等后果。唯一的限制是该漏洞影响范围较小,仅影响1.1.0a版本的openssl,而该版本的openssl发布时间比较晚,实际使用的并不多。笔者对此次漏洞进行了一次详细分析,同时通过漏洞分析分享笔者关于安全的一些思考。漏洞重现此次漏洞仅影响版本为1.1.0.a的openssl,下面让我们一起来一步步重现此次漏洞。漏洞测试的系统为Ubuntu。如果不熟悉linux的朋友建议安装一个虚拟机进行测试。第一步首先我们从github上下载源码并编译:wget&&/openssl/openssl/archive/OpenSSL_1_1_0a.tar.gz&
tar&-xf&OpenSSL_1_1_0a.tar.gz
cd&openssl-OpenSSL_1_1_0a
./config&--debug
make&-j4如果编译成功,可以在apps目录下看到openssl执行程序。在这里我们为了不影响系统原有的openssl,不执行&sudo make install命令,因此需要把生成的动态库文件libssl.so和libcrypto.so放到系统库目录下。sudo&cp&./libssl.so.1.1&/usr/local/lib
sudo&cp&./libcrypto.so.1.1&/usr/local/lib生成一张测试证书./openssl&req&-x509&-newkey&rsa:2048&-keyout&key.pem&-out&cert.pem&-days&365&–nodes运行该命令后openssl会询问一些关于证书的相关信息,无视掉,直接一路enter就好了。使用openssl的s_server子命令搭建SSL服务器,监听20443端口./openssl&s_server&-key&key.pem&-cert&cert.pem&-accept&20443&–www使用nc向openssl的本地20443端口发送异常的ssl握手包nc&localhost&20443&&&send_contentopenssl收到这一畸形的握手包后bang的一声down掉了!怎么样,利用是不是很简单呢?send_content地址:基础知识接下来我们要对漏洞产生的原因和如何构造一个漏洞测试数据包进行学习,但是让我们首先来学习一些关于SSL的基础知识。在漏洞重现中我们搭建了一个ssl服务器,下面我们打开wireshark进行抓包,捕获此次的SSL通信过程。在wireshark中设置过滤条件:tcp.port=20443,避免显示太多无用的通信包。使用firefox与openssl的ssl服务器进行通信。在firefox的地址栏输入:https://localhost:20443此时firefox会提示你该网址不安全,不用理会这一提示,这是因为证书是我们为了测试生成的,不在firefox的可信根证书列表中。依次点击Advanced-&Add Exception-&Confirm Security Exception,确认安全例外网址https://localhost:20443。这时回到wireshark的界面,可以看到wireshark已经抓到了本次ssl通信的数据包。SSL通信的过程是这样的,首先客户端和服务器端经过三次握手建立TCP连接,然后客户端发送的第一个数据包通常被称为“client hello“,意思就是说client想要和server进行通信,首先要向服务器端say一下hello,这个hello包中包括了客户端需要交换的随机数,支持的加密算法等内容,但和本次漏洞相关的是SSL包的长度,就是图中标红的两个length,512和508,标明了SSL数据段的长度,正是因为openssl对长度的处理不当导致了此次漏洞。漏洞分析目前,openssl已经发布了漏洞补丁,我们先来看看补丁():+static&int&grow_init_buf(SSL&*s,&size_t&size)&{
+&&&&size_t&msg_offset&=&(char&*)s-&init_msg&-&s-&init_buf-&
+&&&&if&(!BUF_MEM_grow_clean(s-&init_buf,&(int)size))
+&&&&&&&&return&0;
+&&&&if&(size&&&msg_offset)
+&&&&&&&&return&0;
+&&&&s-&init_msg&=&s-&init_buf-&data&+&msg_
+&&&&return&1;
&&*&This&function&implements&the&sub-state&machine&when&the&message&flow&is&in
&&*&MSG_FLOW_READING.&The&valid&sub-states&and&transitions&are:
@@&-545,9&+560,8&@@&static&SUB_STATE_RETURN&read_state_machine(SSL&*s)
&&&&&&&&&&&&&/*&dtls_get_message&already&did&this&*/
&&&&&&&&&&&&&if&(!SSL_IS_DTLS(s)
&&&&&&&&&&&&&&&&&&&&&&&&s-&s3-&tmp.message_size&&&0
-&&&&&&&&&&&&&&&&&&&&&&&!BUF_MEM_grow_clean(s-&init_buf,
-&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&(int)s-&s3-&tmp.message_size
-&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&+&SSL3_HM_HEADER_LENGTH))&{
+&&&&&&&&&&&&&&&&&&&&&&&!grow_init_buf(s,&s-&s3-&tmp.message_size
+&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&+&SSL3_HM_HEADER_LENGTH))&{
&&&&&&&&&&&&&&&&&ssl3_send_(s,&SSL3_AL_FATAL,&SSL_AD_INTERNAL_ERROR);
&&&&&&&&&&&&&&&&&SSLerr(SSL_F_READ_STATE_MACHINE,&ERR_R_BUF_LIB);
&&&&&&&&&&&&&&&&&return&SUB_STATE_ERROR;上面以-开始的行意味着从源码中删除,以+号开始的意味着向源码中增加。分析一下该补丁,补丁为BUF_MEM_grow_clean函数的调用增加了一层封装grow_init_buf。接下来我们用GDB来实际调试一下:gdb&–args&./openssl&s_server&-key&key.pem&-cert&cert.pem&-accept&20443&–www在补丁对应的行上下断点:b&statem.c:546朋友们可以手动跟一下函数运行的流程。对比一下正常的TLS握手包和畸形的TLS握手包对于openssl的运行流程有什么区别。引发漏洞的根源在BUF_MEM_grow_clean函数中,该函数位于源码crypto/buffer/buffer.c文件中,我们来重点分析一下这个函数的流程。在BUF_MEM_grow_clean函数中,有两个入参,第一个是openssl分配的结构,用于记录为此次clienthello包分配的内存的相关信息,第二个入参是数据包的长度,而这一长度是从我们传入的数据包中获得的,这也就意味着该参数是攻击者可控的。以下是BUF_MEM_grow_clean的代码。size_t&BUF_MEM_grow_clean(BUF_MEM&*str,&size_t&len)
&&&&char&*
&&&&size_t&n;
&&&&if&(str-&length&&=&len)&{&&&&&&&&&&&&&
&&&&&&&&if&(str-&data&!=&NULL)
&&&&&&&&&&&&memset(&str-&data[len],&0,&str-&length&-&len);
&&&&&&&&str-&length&=&
&&&&&&&&return&(len);
&&&&if&(str-&max&&=&len)&{&&&&&&&&&&&&&&&
&&&&&&&&memset(&str-&data[str-&length],&0,&len&-&str-&length);
&&&&&&&&str-&length&=&
&&&&&&&&return&(len);
&&&&/*&This&limit&is&sufficient&to&ensure&(len+3)/3*4&&&2**31&*/
&&&&if&(len&&&LIMIT_BEFORE_EXPANSION)&{&&&&&&&&&&&&&&
&&&&&&&&BUFerr(BUF_F_BUF_MEM_GROW_CLEAN,&ERR_R_MALLOC_FAILURE);
&&&&&&&&return&0;
&&&&n&=&(len&+&3)&/&3&*&4;
&&&&if&((str-&flags&&&BUF_MEM_FLAG_SECURE))
&&&&&&&&ret&=&sec_alloc_realloc(str,&n);&&&&&&&
&&&&&&&&ret&=&OPENSSL_clear_realloc(str-&data,&str-&max,&n);
&&&&if&(ret&==&NULL)&{
&&&&&&&&BUFerr(BUF_F_BUF_MEM_GROW_CLEAN,&ERR_R_MALLOC_FAILURE);
&&&&&&&&len&=&0;
&&&&}&else&{
&&&&&&&&str-&data&=&
&&&&&&&&str-&max&=&n;
&&&&&&&&memset(&str-&data[str-&length],&0,&len&-&str-&length);
&&&&&&&&str-&length&=&
&&&&return&(len);
}通过使用GDB跟踪openssl对畸形TLS数据包的处理,该TLS握手包的的长度段的值必须同时不满足代码1,代码2,代码3的判断并进入代码4处。即漏洞被触发需要同时满足str-&length & len,str-&max & len,len & LIMIT_BEFORE_EXPANSION,三个条件。其中str-&length也是由攻击者传入的,str-&max和LIMIT_BEFORE_EXPANSION都是固定的。str-&max的值为2b4)LIMIT_BEFORE_EXPANSION的值定义在/crypto/buffer/buffer.c:19#define LIMIT_BEFORE_EXPANSION 0x5ffffffc。满足这三个条件后函数进入代码4处,使用realloc函数重新分配一块内存,而导致原先的str-&data指针被free掉,成为野指针,而程序其他地方继续使用这一指针,就导致了Use After Free。至此为止,漏洞的原理搞清楚了,那么如何构造畸形的TLS握手包呢?首先使用wireshark导出正常的TLS握手包,将包头中的两个长度段分别改为0x4000, 0x5560。并在包尾填充相应长度的字符。很简单,是不是?漏洞溯源俗话说,”冤有头,债有主”,那么这次漏洞是如何出生的呢?openssl使用的代码管理工具是git,我们能够在github看到过往的历史提交记录,让我们来查查此次漏洞到底是如何产生的。根据上面的分析,我们知道漏洞是在statem.c文件中。经过一番搜索,最后找到这段代码的修改记录:查看页面中的修改说明,openssl给TLS包分配内存的时机太早,如果有恶意攻击者发送大量恶意TLS包,可能导致openssl分配大量内存而导致拒绝服务漏洞。注意看说明结尾,此次漏洞是由360团队的shilei向openssl报告的,openssl开发团队收到这一漏洞报告后对相关文件进行了修改,并最终导致了此次拒绝服务攻击。总结起来就是360的一位安全研究员shi lei向openssl报告了CVE-拒绝服务攻击漏洞,openssl对此进行了修改,并导致了CVE-漏洞。通过这一漏洞的分析,以下是笔者一些不成熟的关于软件安全的想法,请大家指正:1.尽量不要让你的代码太复杂。我认为软件开发人员在修复一个bug的时候引入一个新bug的原因在于软件的复杂度已经超过了开发人员的驾驭能力。对于开发人员而言,太过复杂的代码容易出bug,这是常识。但是因为业务的各种变更,项目进度需要,历史原因等种种实际情况,很容易出现极其复杂的代码,并产生安全漏洞。因此,开发人员如果有多一些时间的话,希望能思考一下,你手头上正在开发或维护的代码,能否在不影响业务的基础上降低复杂度。2.在给漏洞打补丁的同时,也可能产生新的漏洞。安全研究人员在挖掘漏洞的时候,可以试着从软件的补丁上考虑。
本文由 安全客 原创发布,如需转载请注明来源及本文地址。本文地址:/learning/detail/3314.html
参与讨论,请先
软件复杂度确实会带来意向不到的问题,不单单是安全问题,还有功能问题。
安全播报APP
Copyright & 360网络攻防实验室 All Rights Reserved 京ICP证080047号[京ICP备号-6]https详细介绍
使用https技术是为了让我们的用户在访问我们的网站的时候不会被第三方窃听、篡改内容,最大程度上的保证内容传输的安全
HTTPS是在HTTP下加入SSL(Secure Sockets Layer安全套接层)层,HTTPS的安全基础是SSL,因此的详细内容就需要SSL。
SSL,及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。
SSL Handshake Protocol用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等
SSL Record Protocol 它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加解密等基本功能的支持。
加密算法是SSL/TLS最重要的组成,没有它们就不会有SSL/TLS协议的诞生,加密算法主要分为对称加密算法和非对称加密算法,对于文本加密一般采用对称加密,而对于秘钥的管理则采用非对称加密,目前主流的对称加密算法有DES,AES,RC系列,TEA系列, Blowfish等,非对称加密算法有RSA、Diffie-Hellman等,综合起来AES和XXTEA算法性能最好,AES使用更广泛。
HTTPS一般使用的加密与HASH算法如下:
对称加密算法:AES,RC4,3DES
非对称加密算法:RSA,DSA/DSS
HASH算法:MD5,SHA1,SHA256
下面详细介绍各算法的特点:
对称加密算法
对称加密指的是可以使用同一个密钥对内容进行加密和解密。相比非对称加密,它的特点是加、解密速度快,并且加密的内容长度几乎没有限制
现在常用的对称密钥主要分成两种,块式加密和流式加密,他们的基本思想都是对信息进行XOR、移位等操作来进行加密的。在操作方式上块式加密是把message分成多个固定长度的组,每组包含多个字节,每次操作的时候会针对一组字节进行操作,鼎鼎大名的DES和AES就是采用这类方式。而流式加密是每次只针对一个字节进行操作,鼎鼎大名的RC4就是采用这种方式。
DES加密原理
我们将需要加密的消息分成长度为64bits的一堆block,对每个block通过XOR、移位等等方式进行加密,DES的密钥(key)长度是64bits,有效长度是56bits,因为其中的8个bits用于校验。另外DES有一个特点就是对于同一个block,如果使用同一个密钥(key),加密出来的结果是相同的,换句话说攻击者可以通过寻找同样的密文block来推理出原文,这就是我们常说的回放攻击。
为了解决回放攻击的问题,最简单的方式就是CBC(cipher block chaining),简单说来就是每次加密一个block之后,把它和前一个block的密文进行XOR操作,作为这个block的密文,这样即便是同样的block,每次加密的结果也会不一样,攻击者就无法通过密文猜测原文了。另外第一个block因为在它之前没有其他block了,所以我们需要生成一个随机的64bit的intialization vector(类似我们在写程序对用户密码加密的时候搞的salt)给第一个block进行XOR操作。
3.3DES是在DES的基础上将密钥扩充为原来的3倍也就是192bits(实际有效密钥长度是56bits*3=168bits),3DES的加密过程其实就是将密钥分成三份,使用第一份密钥加密、第二份密钥解密、第三份密钥加密,然后解密的话刚好相反,使用第三份密钥解密,第二份密钥加密和第一份密钥解密,通过这种方式去扩展DES的加密长度问题,3DES更加安全,也能抵挡之前说的回放攻击。
3DES虽然很安全,但是因为多次的加解密导致性能其实很低的,所以数学家们希望在同样的密钥长度下使用更高效率的算法,于是有了AES。
AES允许密钥长度为128-bit,192-bit和256-bit,当前来说使用128-bit已经足够安全,并且性能比3DES快很多,所以现在使用最多的对称加密就是AES。
RC4的原理(流式加密)
如果把block的size变成了1 byte那么就变成了stream加密算法,当然具体的实现肯定不一样,block式加密主要是将block里面的每个bit进行移位,而 stream加密是生成同样长度的、安全的字符串,然后再和原文进行XOR生成最终的密文,所以stream的安全性主要取决于这个安全字符串的生成算法,同样因为RC4安全性取决于安全字符串的生成,所以它不需要CBC或者IV。
对称加密的局限性:
使用对称加密,加密和解密都是用同一个密码。这样就存在一个非常大的问题,就是如何把密钥告诉对方。把信息加密的原因就是因为信息传递过程中存在被人窃听的风险,既然信息传递的过程不安全,那么又如何能保证安全的把密钥发送给对方?
非对称加密算法:
非对称加密和对称加密不一样,它有两个密钥,分别为公钥和私钥;公钥是公开给所有人的,私钥永远只能自己知道。使用公钥加密的信息只能使用私钥进行解密同样的使用私钥加密的信息只能使用公钥进行解密。常用的非对称加密算法有RSA、DH(Diffie-Hellman)、RSA,下面主要介绍RSA算法
最常用的非对称加密就是RSA算法,其可靠性来源于对极大整数做因式分解是极其困难的,到现在为止还没有发现快速RSA的方法,只需要密钥足够长(2048bits以上),就可以认为RSA加密的信息是无法破解的。
通信过程:
1)假设A需要向B发送消息,那么B首先用RSA算法生成一个密钥对。然后把私钥自己存储,把公钥发送给A。注意,我们假设最坏的情况,任何通信都是不可靠的。这个过程中我们假设公钥已经被第三个人C截获。
2)A在接收到B的公钥之后,就可以把自己想要发给B的报文用这个公钥进行加密,然后传送给B。B接收到密文之后,用自己的私钥进行解密,就可以知道A发送的报文内容。假设这个过程依然被C监听到,但是,由于C只有公钥而没有B的私钥(因为B自己存储了私钥,没有发送给任何人),所以C无法解密A发送的密文。他能做的只是用自己的公钥加密一些信息发送给B来冒充A,但是永远无法知道A发送给B的内容是什么。
这样就可以保证A向B发送的信息,对任何第三者都是无法解密的。但是B如何向A发送信息呢?很显然,只需要A也生成一对密钥,然后把公钥发送给B就可以了。如果使用RSA算法进行通信,那么在通信内容开始前必然会存在这个交换公钥的过程。之后双方都可以使用对方的公钥进行加密,然后使用自己的私钥解密对方的信息。这个信息传递的可以完全公开,因为其他任何人都没有A和B的私钥,所以都无法解密。
中间人攻击:
上述的RSA算法看似非常完美,可以在不可靠的通信上建立可靠的加密通信。那么这个做法没有任何吗。答案是否定的。非对称加密通信也存在一个致命的弱点,就是它有一个交换公钥的过程。假设现在第三者C可以随意修改A和B之间的通信内容。C可以这么做:
A和B告诉对方,我们要使用RSA算法进行加密通信。C监听到这个信息。A和B分别生成一对密钥,假设A的密钥是(a_pri/a_pub),B的密钥是(b_pri/b_pub),把私钥a_pri和b_pri自己保存。这时,C也自己生成了两对密钥(ca_pri/ca_pub, cb_pri/cb_pub)。A把公钥a_pub发送给B,C截获这个报文,把a_pub保存,然后把自己的ca_pub发送给B,B会以为这是A的公钥。B把公钥b_pub发送给C,C截获这个报文,把b_pub保存,然后把自己的cb_pub发送给A,A会以为这是B的公钥。B把报文用ca_pub加密发送给A,C截获这个报文,然后用ca_pri进行解密,然后修改报文,并用 a_pub 进行加密,发送给A。A接收到密文,用ca_pri 解密。A把报文用cb_pub加密发送给B,C截获这个报文,然后用cb_pri进行解密,然后修改报文,并用 b_pub 进行加密,发给B。简单的说,就是C扮演了一个中间人的角色,他拦截了AB交换公钥的过程,并用自己的公钥替换了A和B的公钥。然后他就可以对A扮演B,对B扮演A。而A和B完全不知道这个C的存在,他们会以为自己的通信是安全的。
数字签名和CA:
可见,C之所以能冒充A和B,是因为A和B交换公钥的过程无法保证收到的公钥未经篡改。解决这个问题,我们只需要对公钥进行数字签名,就可以防止公钥被篡改。下面我们先看看数字签名的实现原理。
数字签名有不同的算法,最简单的一种就是直接用RSA算法实现的RSA签名。它直接用私钥加密信息,然后用对应的公钥来解密(就是验证)信息。这和上面的RSA通信过程刚好相反。
你会发现这个上面AB通信的时候的做法差不多,不同的是公钥和私钥的用法是相反的。因为我通信的时候是不希望别人看到通信内容,所以公钥是用来加密的。而我做数字签名的时候,是不希望别人冒充我的签名,但是希望别人都知道怎么鉴定这个签名是不是我的(解密),所以这时候的私钥是用来加密,而公钥是用来解密的。
因为很可能需要发送的信息量很大,比如是一个很大的文件。对这个文件内容做RSA计算是很费时的,所以可以先把文件做一次哈希(提取摘要),然后把哈希值做一个数字签名。这样接收方只需要用公钥解密哈希值,然后由哈希值来判断文件内容是否被篡改就行了。
那么,RSA结合HASH的数字签名技术对公钥进行签名,就很容易防止AB在交换公钥的过程中被其他人篡改公钥。
但是问题来了,我怎么能知道用来验证签名的公钥是不是可信的?
于是我们就需要一个大家都认可的有公信力的组织来颁布证书,这就是CA(Certificate Authority)。
A像CA提供他的证明材料,然后CA审核通过之后,制作一个用来生成签名的证书给A,同时公开发布一个用来验证A签名的证书。
具体过程是这样的:
A 向CA提供证明材料,申请证书。CA审核通过之后,用RSA算法生成一对密钥。CA用私钥制作一个证书文件发送给A,这个文件是用来生成签名的,只有A知道。CA用公钥制作另一个证书,并公开,任何人都可以获取这个证书,并且可以用这个证书来验证一个文件是否被A签名。A用自己的证书把公钥签名并发送给B。B去CA下载A的证书,然后用这个证书来验证公钥是否是A签发的,如果中途被C替换了公钥,那么这次验证就会失败,就可以知道交换公钥的过程被篡改了。
大部分时候,我们在中访问HTTPS的网站的时候用到了数字证书,而知名机构,比如google用的证书是直接预装在浏览器中的,所以我们一般是不需要自己下载证书的。
消息摘要(Message Digest):
刚才说到我们可以对明文进行sha1、md5等摘要计算,通过对摘要的加密和验证来保证消息来源的准确性,所以摘要算法有几个特点:
压缩性:任意长度的数据,算出的MD5值长度都是固定的。容易计算:从原数据计算出MD5值很容易。抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。弱抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。
RSA在传输加密信息的时候非常好用&&公钥加密,私钥解密数字签名使用的是RSA的验证功能&&也就是私钥加密签名信息,公钥解密后验证签名来确保消息发送来源是真实没有被篡改所以有了RSA,我们只需要可以将我们的公钥让所有人都知道,那么希望和我们进行安全通信的人就可以使用我们的公钥加密之后传输给我们,而我们使用私钥解密之后就可以获得原文信息了。因为RSA对加密内容有长度限制而且性能非常低,所以我们一般使用RSA去传输AES或者RC4等对称密钥,然后使用对称密钥对我们要传输的内容进行加解密。
SSL&TLS过程
有了上面的介绍,下面看看SSL/TLS具体是如何实现安全传输的:
客户端发送一个ClientHello消息,消息里面会生成一个随机数,同时告诉服务端自己支持的SSL版本,加密算法等信息。
服务端接收到客户端的ClientHello消息之后也会生成一个随机数,然后选择SSL版本,加密算法等参数,把这些内容放到ServerHello消息里面发送给客户端。
服务端接着把自己的证书发送给客户端,证书里面包括服务端的公钥,第三方证书认证机构(CA)的签名,服务端的域名信息等内容,这个阶段叫做Certificate。
ServerKeyExchange是一个可选的过程,如果在ServerHello的时候服务端选择了RSA加密作为密钥交换的话就没有这个过程,如果服务端选择的密钥的交换方式是DHE或者ECDHE等双方共同计算出来的密钥的话(而不是通过RSA加密后传输的话),那么这个阶段就是双方进行协商的阶段,服务端会提供他希望使用的p、g参数以及使用服务端自己计算出来的Ys,当然自己会留一个私有的参数(这几个参数的意思参考前面说的Diffie-Hellman)。
ServerHelloDone表示服务端完成了它这一块的协商过程。
ClientKeyExchange这个阶段用户会首先认证服务端的证书是否合法(使用CA的公钥对之前CA颁发的数字签名进行验证),验证通过之后就去计算Pre-Master Key,然后使用服务端证书对Pre-Master Key加密后传输给服务端(如果是DH的话就是协商的p、g以及客户端自己计算出来的Yc,并且根据之前服务端提供的Ys数据计算出Pre-Master Key);服务端收到这个内容后使用自己的私钥进行解密得到Pre-Master Key(如果是DH的话就是通过p、g、Yc以及自己的一个随机数计算出Pre-MasterKey),最终双方都能拿到或者计算出Pre-Master Key,通过Pre-Master Key我们会生成MasterKey,最终在生成对称密钥。
ChangeCipherSpec阶段表示发送完密钥信息之后客户端就进入了加密模式并且通知服务端我已经进入加密模式了
客户端然后会对client finished加密后发送给服务端。
服务端收到加密后的client finished消息之后使用之前计算出来的MasterKey进行解密,如果解密成功那么同样也进入ChangeCipherSpec阶段,通知客户端我也进入加密模式了
然后服务端也会发送一个server finished消息,如果客户端能够成功解密,表示双方的密钥协商已经全部完成,之后就可以将数据使用对称密钥加密后进行传输了。特别注意的一点是在Finished消息中会带上一个verify_data字段,这个字段会对整个Handshake进行hash,为的是防止我们在协商过程中的数据被人篡改了。
CA证书认证体系
简单来说服务端会把自己的公钥以及别的一些信息例如域名提交给一个知名的第三方CA(Certificate Authority),由CA使用自己的私钥进行数字签名生成证书;而因为这个CA是大家都认可的,它的证书已经被很多操作、浏览器默认都安装了。这时候当客户端收到服务端的证书的时候,他就是用安装在操作系统内的CA证书里面的公钥去进行解密,发现域名信息显示确实是服务端的。如果第三方攻击者把自己的证书给了客户端,而客户端使用CA的公钥去解密验证的时候,发现解密出来的结果显示的不是服务端的域名而是第三方攻击者的域名,所以就不信任了。
那第三方攻击者能否让自己的证书显示出来的信息也是服务端呢?这个是不行的,因为当第三方攻击者去CA那边寻求认证的时候CA会要求其提供例如域名的whois信息、域名管理邮箱等证明你是服务端域名的拥有者,而第三方攻击者是无法提供这些信息所以他就是无法骗CA他拥有属于服务端的域名
如何生成对称密钥
密钥生成的阶段一般包括三个阶段,首先生成Pre Master Key(如果是RSA作为KeyExchange的话客户端就会将这个密钥传输给服务端,如果是DH算法的话就是客户端和服务端各自生成Pre Master Key),然后使用Pre-Master加上Hello阶段的两个random bytes随机数生成masterkey,最终再由masterkey生成加解密对称密钥
Pre-Master key的生成方式,他使用了一种叫做Pseudorandom Function随机数生成算法,简称PRF,一般由SHA256和HMAC组成,在TLS1.2中要求显示指定PRF。
生成Pre-Master Key之后我们还需要使用PRF生成Master Key:
master_key = PRF ( pre_master_key, &master secret&, ClientHello.random + ServerHello.random)
为什么需要生成masterkey而不直接使用Pre-Master Key?两方面的原因:一方面是因为不同的KeyExchange方式导致生成的Pre-Master Key的长度不一样,另外一方面是因为通过这样的方式保证足够随机。
生成MasterKey之后我们还得最终生成一组密钥,这一组密钥就是后续我们用来加密信息进行传输的密钥:
key_block = PRF (master_key, &key_expansion&, ClientHello.random + ServerHello.random)
这个key_block会被分成6个key,其中两个作为MAC key,两个作为encryption key,两个作为IV(主要用在CBC算法里面)。
Encryption Key分成client write key和server write key,分别用来加解密客户端发送的数据和服务端发送的数据
MAC key分成client write MAC key和server write MAC key,主要用来验证客户端发送的数据和服务端发送的数据没有被截断(这个很重要,想想虽然别人可能没有办法修改、查看你的加密数据,但是他可以把你加密的数据砍掉一半传输给另外一方,而另外一方可能还是可以正常解密,但是无法发现数据被砍断了。)
这里需要提及一下,在TLS1.2里面,Session复用的时候客户端和服务端都会存下Master Key,但是因为生成key_block的时候每次都会使用random数,所以最终生成的密钥都是不一样的。
[转]HTTPS连接最初的若干毫秒
原文地址:
当你在浏览了一个网站上面的商品之后,点击&继续并结帐&时会发生什么?本文即将对(浏览器)与Amazon建立安全连接的整个过程中最初的若干毫秒进行分析。当你点击继续按钮时一个新的页面将被加载:
在短暂的220毫秒内,发生了很多有趣的事情,Firefox修改了地址栏的颜色,并在其右下角放置了一个锁状的图标。在我最喜爱的网络工具Wireshark以及略微修改的Firefox调试版的帮助下,我们可以对正在发生的事情看个究竟。
根据RFC 2818协议的规定,Firefox明白&https&意味着它应该连接的443端口:
大多数人将HTTPS和SSL(Secure Sockets Layer)联系起来,SSL是Netscape公司在90年代中期发明的。随着时间的推移这种说法就渐渐变得不准确了。由于Netscape失去了市场份额,它将SSL的维护工作移交给因特网工程任务组(IETF)。第一个后Netscape版本被重新命名为安全传输层协议(TLS),TLS1.0是在1999年1月份发布的。由于TLS诞生都10年了,所以真正的&SSL&传输其实是几乎见不到。
Client Hello
TLS将所有的网络传输打包成不同的&记录&类型。我们看到从浏览器出来的第一个字节是一个十六进制(hex)字节0x16=22,这说明它是一个&握手&记录:
后面的两个字节是0x0301,意味着它的版本是3.1,事实上TLS1.0就是SSL3.1。
&握手&记录被分解成若干消息。第一个就是&Client Hello&消息(0x01)。这里面有很重要的几点:
随机数:前面的四个字节是当前的协调世界时(Coordinated Universal Time,UTC),它的格式是Unix时间戳,也就是从日起到此刻所经历的秒数。在本例中的该数字是0x4a2f07ca。跟随其后是28字节的随机数,它将在后面使用。
会话标识(session id):在这里它是空值或者是null。如果在几秒前该浏览器曾连接过,它就可能继续使用前面的会话,而不需要重新执行整个&握手&过程。
密码套件:它是浏览器所支持的密码算法的一个列表。最上面的是一个很强大的组合&TLS_ECDHE_ECDSA_与_AES_256_CBC_SHA,下面还有该浏览器支持的另外33个选择。如果现在不明白它们的含义也不必担心,后面你将会发现Amazon并没有选择最上面的强大组合。
server_name extension: ?通过它告诉,浏览器正要连接,这样做非常方便,因为TLS&握手&发生在所有的HTTP传输之前。HTTP协议中有一个&Host&头,这就允许了Internet托管公司出于成本的考虑将上百个网站绑定在同一IP 地址上。传统意义上SSL要求每一个地址有一个不同的IP,而这个扩展就使得服务器能够正确响应浏览器所请求的(服务器)证书。最后注意一点,对于 IPv4的地址这一扩展的有效期大概能再多一周左右。
Server Hello
对应于Client Hello,也返回一个&握手&记录,它是两个庞大的数据包(2,551字节)。该记录的版本号是0x0301,也就意味这Amazon支持我们使用的TLS1.0版本的请求。这个记录分成三个子消息,他们包含着一些有趣的信息。
1.&Server Hello& 消息(2):
我们得到服务器返回的四字节的Unix时间戳以及28字节的随机数,它也将在后面使用(译注:注Clien Hello是发了一个随机数,这两个随机数将用于产生最后的对称密钥)。一个32字节的会话标识,有了它,随后重连服务器就不需要再执行一个完整的握手过程了。从我们提供的34个可选的密码套件中,Amazon选择的套件&TLS_RSA_WITH_RC4_128_MD5&(0x0004)。也就是说它将使用&RSA& 公钥算法来验证证书以及交换密钥,用RC4加密算法对数据进行加密,使用MD5哈希算法来校验消息内容。后面会对它们做详细介绍。我个人认为Amazon选择这个密码套件的原因是出于自私的考虑(译注:这里说&自私&的意思是,Amazon出于性能的考虑,降低了安全的强度)。这个套件是这34个套件中消耗CPU最少的一个,因此Amazon选择它就是为了节省一些CPU来处理更多的用户连接。还有一个不大可能的原因是他们要特别地向Ron Rivest致敬,这个套件中所用到的三个算法都是他发明的。
2、证书消息(11):
这个巨大的消息占据了2464个字节,它是一个证书,客户端用它对Amazon进行认证。它并不是什么花哨的玩意,通过浏览器就可以看到它的大部分内容:
3、&Server Hello 结束&消息(14):
这是一个0字节的消息,它告诉客户端&hello&过程已经完成,也就意味着服务端将不验证客户端的证书。
浏览器必须要确定它是否要信任,在这里是通过证书判断的。它检查Amazon的证书并且检查现在的时间是否在证书的有效期之内,该有效期规定的是&不能早于&日且&不能晚于&日。此外,它还要确保该证书的公钥已经被授权在进行密钥交换的过程中使用。
为什么我们应该相信这个证书呢?
在证书上附有一份&签名&,这个签名事实上是一个big-endian(译注:Big-Endian和Little-Endian是一种二进位资料储存或传输的格式,Big-Endian的最高位字节在最前头,而Little-Endian的最高位字节在最后面)格式的长整型数:
任何人都有可能发给我们这些字节,为什么我们要相信这个签名呢?为了回答这个问题,有必要先到这里mathemagic land去速成一些相关知识:
穿插:简短的,不那么可怕的RSA向导
有时候人们想知道数学和有何联系。证书就是应用数学的一个非常实际的例子。Amazon的证书告诉我们应该用RSA算法来校验其签名。RSA是MIT的教授Ron Rivest,Adi Shamir和Len Adleman在70年代创造出来的,他们三人创造了一个巧妙的方法将2000多年来; 数学发展的思想汇合起来形成了一个漂亮而简单的算法:
首先选择两个很大的质数&p&和&q&,并对他们求积得到&n=p*q&。接下来,取一个较小的&e&作为指数,它用作&加密指数&,而对e的进行特殊的逆反函数计算所得到的&d&作为&解密指数&。然后将&n&和&e&公开出去,而对&d&要保密,对于&p&和&q&你可以把它们扔掉,也可以像&d&一样保密起来。真正重要的要记住&d&和&e&是相互的逆反。
现在,如果你有一些消息,那么你只需要将该消息的字节翻译成一个数&M&,若要对这个消息进行&加密&形成&密文&的话,你就这么计算:
C & Me(mod n)
它意思是先求&M&的&e&次方,然后对它应用模数&n&求余。举个例子,11AM +3 hours & 2 (PM)(mod 12 hours)。接收者知道(解密用的)&d&,而&d&可以对已加密的消息进行反转并还原消息:
Cd & (Me)d & Me*d & M1 & M (mod n)
另一件有意思的事情是拿着&d&的人可以对一个文档进行签名,其做法是文档&M&求&d&次幂然后应用模数&n&求余数&S&:
Md & S (mod n)
这种做法成立的原因是&签名者&将&S&,&M&,&e&以及&n&公开出去,任何人都可以通过这样一个简单的计算来验证&S&是否由&签名者&所签(译注:因为每个&e&都是某个签名者所独有的,而&e&和&d&是成对出现的,所以签名者用自己的&d&签名,只能拿这个&d&所对应的&e&才能还原消息,这就到了签名的作用):
Se & (Md)e & Md*e & Me*d & M1 & M (mod n)
像RSA这样的公钥密码算法经常被称之为&非对称&算法,因为加密的密钥(本例子中的&e&)和解密的密钥是不相同的(本例子中是&d&)。对所有的数应用&mod n&的目的是使攻击者不可能使用简单的技术(如过去我们使用的对数)破解它。RSA的神奇之处是你可以很快计算/加密C & Me (mod n),而在不知道&d&的情况下计算/解密Cd & M (mod n)是非常困难的。如前面所说,&d&来自于对&n&的因子分解,而&n&来自于&p&和&q&,求解&d&是一件复杂的事情。
在现实世界里使用RSA时应该谨记的重要的一点是,前文提到的所有数字都应是很大的数,只有这样才能保证,即使用目前最好的算法也很难攻破上述算法。到底多大才算很大的数呢?的证书是由&VeriSign Class 3 Secure Server CA.& 签名的。从这个证书中我们可以看出,VeriSign所使用的模数&n&是一个2048比特长的数字,表示成10进制数的长度是617位:
(如果你要试图从这个&n&中找到&p&和&q&那就祝你好运,假如你能找到他们,那么你能产生像真的一样的VeriSign证书了)
VeriSign的&e&的值是2^16 + 1 = 65537。当然,他们对相应的&d&保密,也许被放在一个安全的硬件设备上,由视网膜扫描机和武装部队保护着。在签名之前,VeriSign要通过现实世界的&握手&来验证声明的证书内容,包括审查他们的很多商务文档。一旦VeriSign对这些文档满意了,他们使用SHA-1哈希算法对包含所有声明的证书进行计算获得一个哈希值。在Wireshark中看到整个证书的样子如下图&signedCertificate&部分所示:
上面的说法(整个证书如同signedCertificate所示)说有点用词不当,因为证书的实际含义是签名者将要进行签名的部分,而不是已经包含了签名信息的字节。
实际的签名,也就是&S&,是Wireshark包中简单地称为&encrypted&的部分(见上图)。如果我们求&S&的&e&次幂(&e&是VeriSign公开的),然后在对该结果取模&n&并得到余数,那么我们得到的&已解密&十六进制签名是:
0001FFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFF052B0E014C19FC60EFE7C830539DB
基于PKCS #1 v1.5标准,最开始的字节是&00&,它&保证了加密块被转换成整数之后,大小不会超过模数&。第二个字节&01&说明这是一个私钥操作(如,它是一个签名)。后面是一大堆&FF&,用他们填充这个结果,使之足够大。填充字节以&00&结束,后面跟的是&30 21 30 09 06 05 2B0E 03 02 1A 05 00 04 14&,PKCS #1 v2.就是这样来指定SHA-1哈希算法。最后的20个字节是对&signedCertificate&部分的SHA-1哈希摘要。
因为解密的值有着正确的格式并且最后的字节和我们独立计算得到的哈希值相同,我们可以认为这就是由拥有&VeriSign Class 3 SecureServer CA&私钥的人所签名的,而我们隐含地信任只有VeriSign知道这个私钥&d&。
但我们为什么要信任它呢?因为在信任链上已经没有更高级别了。
最上层的&VeriSign Class 3 PublicPrimary Certification Authority&是自签名的。这个证书自从服务(Network SecurityServicesNSS)库中certdata.txt的1.4版本之后就已经被嵌入Mozilla的产品中作为一个隐含的受信证书。它是由NetScape的Robert Relyea在日登记进去的,当时他还留下以下评注:
&用这个框架来收集其他网络安全服务,它包括这一个&实时的&certdata.txt ,里面存放了那些我们已经获得许可权可以推向开源的证书(当我们从所有者那里获得许可权之后,会有更多的证书加入这个文档中)&
由于该证书的有效期范围是从日到日,所以这个决定是意义深远的。
Ken Thompson在他的&Reflection on TrustingTrust&中解释的非常好,你最终不得不隐式地信任某人。在这个问题上别无选择。本例中我们隐含地信任Robert Relyea做了个好的选择,我们也希望Mozilla的内建证书策略对其他的内建证书也是合理的。
这里要记住的一点是所有这些证书和签名都简单地用来形成一个信任链。在公共网络中,在你还未上任何网站之前,Firefox就已经隐含地信任了VeriSign的根证书。在一个公司内,你可以创建你自己的根证书CA,并将它安装在每个人的电脑上。
或者,你还可以付钱给VeriSign这样的公司并把所有的证书信任链的工作交给它。证书用来通过可信的第三方(这里是VeriSign)建立信任关系。如果你可以安全地和别人共享密钥,比如&在某人耳边轻声告诉他一长串密码&,那么你就可以使用预共享密钥(PSK,Pre-shared Kay)认证来建立信任关系。TLS对此也有扩展,比如TLS-PSK,以及我本人所喜欢的TLS的安全远程密码扩展。然而,这些扩展没能被广泛地使用和支持,所以他们通常是不切实际的。此外,这些方法增加了负担,它们要求必须通过安全的方式交换密钥,而这比通过TLS来建立信任连接的负担更重(要不然我们何不都这么用呢?)。
我们要做的最后的检查是验证证书上的主机名是否是我们期待访问的。 Nelson Bolyard 在 SSL_AuthCertificate 函数中的注释是这样解释的
/* 证书是没问题的。这是SSL连接的客户端。
*现在要检查证书中的名字和所期待的主机名是否一致。
*注意:这是我们防范中间人(Man-In-The-Middle)攻击的唯一途经*/
这个检查防范了中间人攻击,因为我们隐含地相信拥有证书的人不会干坏事,例如,只有它真是,它才会对一个证书签名说它是。如果一个攻击者能够通过类似 DNS缓存破坏的技术修改我们的DNS服务器,你可能被蒙蔽了,以为自己正访问正确的网站(如),因为你的浏览器的地址栏看起来就是这个网站。这最后的检查就隐含地信任证书的授权机构可以防止坏事的发生。
Pre-Master Secret
现在我们已经验证了的那些声明,并且知道其公开的加密指数&e&和模数&n&。任何在网络上侦听的人当然也知道这两个数(这点很明显,因为我们是通过Wireshark获取它们的,别人也可以)。现在我们要创建一个攻击者或偷听者不能辨别的随机密钥,这件事做起来不像听起来那么简单。在1996年,研究者发现Netscape Navigator1.1仅使用三个数据源来作为他们的伪随机数生产器的种子(PRNG),它们是:当前时间,进程号和父进程号。研究显示,这些&随机&源并不那么随机,而且相对比较容易被猜出来。
由于其他的所有东西都是从这三个&随机&源产的,在1996年当时的机器上可能只需25秒中就可以&攻破&SSL&安全&。如果你还不相信寻找随机数是件困难的事,只要问问Debian OpenSSL维护者就知道了。如果随机数搞乱了,那么基于它之上的所有安全都是可疑的。
在Windows上,用于加解密的随机数是通过调用CryptGenRandom函数对来自125个源的抽样数据求hash值产生的。Firefox将该函数产生的随机数和它自己的函数产生的比特码一起作为伪随机数生成器的种子。
虽然这个48字节的&pre-master secret&随机数并不直接使用,但是保证其私密性是非常重要的,因为很多东西都是由它而来的。毫无疑问,Firefox让我们很难看到这个数。我不得不编译了一个调试版本并设置SSLDEBUGFILE和SSLTRACE两个环境变量才能看到它。
在本次会话中,SSLDEBUGFILE中显示的这个pre-master secret是这样的:
4456: SSL[]: Pre-Master Secret [Len: 48]
0301 bb 7b
de e8 e9 b8 9152 ec 81 ...{...I.....R..
4c c2 397b f6 ba 1c 0a b1 955029 be 02 ad e6 L.9{......P)....
ad 6e 113f 20 c4 66 f0 6422577e e1 067a 3b .n.? .f.d&W~..z;
需要指出的是这并非全是随机数,按约定,最前面的两个字节是TLS版本(03 01)。
现在要把这个密钥传给。由于Amazon选择的是&TLS_RSA_WITH_RC4_128_MD5&,我们将使用RSA算法来做这件事。输入消息可以是正好48字节的pre-master secret,但是公钥密码学标准(PKCS)#1,RFC1.5 要求用一些随机数来填充这个数使其长度等于模数的长度(1024比特,或者128字节)。这样做就使得攻击者更难找出我们的pre-master secret了,另外,万一有人做傻事,比如重用相同的密钥,它还为我们提供最后一层保护。通过这样一填充,即使我们重用密钥,偷听者在网络上两次看到的数也是不一样的。
又,因为Firefox让我们很难看到这些随机数,我不得不在填充函数中加入一些调试语句才能看到发生的事情:
Firefox拿这个数进行计算&C & Me (mod n)&,得到的数是我们在客户密钥交换记录中所看到的
最后Firefox发出最后一条没加密的消息,一个&Change Cipher Spec&记录:
Firefox使用这种方式通知Amazon,它将使用前面协商好的密钥来加密下一条消息。
生成Master Secret
如果前面所有事情都正确完成,则双方(且只有这双方,客户端和服务端)现已知道这个48字节的pre-master secret。从Amazon的角度来看这里有一个很小的信任问题:pre- master secret只包含客户端生成的比特位,他们没有考虑到服务端或者我们前面所提到的那些随机数(译注:Client hello和Server hello过程中产生的随机数)。我们将通过计算&master secret&来解决这个问题。根据规约,应该这么计算:
master_secret = PRF(pre_master_secret,&master secret&, ClientHello.random + ServerHello.random)
&pre_master_secret&是客户端之前发送的;&master secret&用的是一个字符串的ASCII值(例如:&6d 61 73 74 65 72 ...&)。然后我们将本文最开始看到的ClientHello和ServerHello发送的随机数拼接起来。
PRF是一个&伪随机数函数&,这个函数很聪明,在规约中也有定义。它使用基于哈希的消息验证码(HMAC)的MD5和SHA-1两种哈希函数将密钥,ASCII字符以及我们给的种子结合起来。对每个哈希函数发送一半的输入。说它聪明的原因是即使面对MD5和SHA-1的弱点,它的防攻击能力还很强。这个过程可以自我反馈并不停地循环,而且我们要多少字节就能生成多少。
依照这个过程,我们获得以下48字节的&master secret&:
4C AF 20308F 4C AA CF2 AC 100039 DB 1D E0 1F CB E0 E0 9D D7 E6 BE 62A46C 1806 AD 7921 DB 821D 5384 DB 35 A7 1F C10119
多个密钥的生成
现在双方都有了&master secrets&,规约描述了我们如何生成会话所需的所有的密钥,我们需要使用PRF函数来创建一个&key block&,然后从这个块中提取所需的密钥:
key_block = PRF(SecurityParameters.master_secret, &key expansion&,SecurityParameters.server_random + SecurityParameters.client_random);
&key_block&被用来提取以下密钥:
client_write_MAC_secret[SecurityParameters.hash_size]
server_write_MAC_secret[SecurityParameters.hash_size]
client_write_key[SecurityParameters.key_material_length]
server_write_key[SecurityParameters.key_material_length]
client_write_IV[SecurityParameters.IV_size]
server_write_IV[SecurityParameters.IV_size]
由于这里使用的是序列密码而非分组密码(如高级加密标准AES),所以不需要初始向量(Initialization Vectors IV),而只是双方各需要一个16字节(128比特)的消息验证码(Message Authentication Code MAC),这是因为指定的MD5哈希摘要大小是16字节。另外,RC4加密算法使用的16字节的密码也是双方都需要的。最后,我们需要key block中的2*16 + 2*16 = 64个字节:
运行PRF,我们得到:
client_write_MAC_secret = 80B8 F6095174 EA DB 2928 EF 6F 9A B8 81B0
server_write_MAC_secret = 677C 967B 70C5BC 629D 1D 1F 4A A6 798161
client_write_key = 32132C DD 1B A DE E5 6C 524672
server_write_key = C 7C 74 DA 6D B7 340A 91B6 8F A7
客户端发出的最后一条&握手&消息是&Finished message&。这个消息非常巧妙,它不仅能证明没有人篡改了握手过程,还能证明我们确实知道密钥。客户端将所有的&握手&消息放入一个 &handshake_messages&缓存区,然后使用伪随机函数,&client finished&字符串以及MD5和SHA-1哈希运算后&handshake_messages&计算出12字节的&verify_data&:
verify_data = PRF(master_secret, &clientfinished&, MD5(handshake_messages) + SHA-1(handshake_messages))[12]
我们在这个结果的前面加上一个记录头字节&0x14&指明&完成&和长度字节&00 00 0c&指明我们将发送12字节的&verify data&。然后,像所有接下来的加密消息一样,我们要确保解密后的内容没有被篡改。因为选择使用的密码套件是TLS_RSA_WITH_RC4_128_MD5,所以我们将使用MD5哈希函数。
有些人一听到MD5就感到恐慌,因为它存在一些弱点,我原先也很不提倡使用它。然而,TLS很聪明,他并不直接使用MD5,而使用它的HMAC版本。TLS是这样使用MDB进行计算的:
HMAC_MD5(Key, m) = MD5((Key & opad) ++ MD5((Key & ipad) ++ m)
(&指的是异或(XOR),++指的是拼接,&opad&是一串&5c 5c ... 5c&字节,&ipad&是另一串&36 36 ... 36&)。
这里我们对以下内容进行计算:
HMAC_MD5(client_write_MAC_secret, seq_num + TLSCompressed.type +TLSCompressed.version + TLSCompressed.length + TLSCompressed.fragment));
也许你已经看到,我们加入了一个序列号(&seq_num&)和明文消息(这里被称为TLSCompressed)的一些其他属性。序列号可以迷惑攻击者,他可能会在中途把一个先前加密的消息插入。如果他这么干,则序列号一定和我们所期待的不一样,这就保护了我们不受攻击者们扔消息的攻击。
剩下就是加密消息了。
RC4加密算法
从前文已知双方协商的密码套件是TLS_RSA_WITH_RC4_128_MD5。这就意味着我们将使用Ron's Code #4 (RC4)对传输信息进行加密。Ron Rivest开发了基于256字节的密钥生成随机数的RC4加密算法。这个算法非常简单,以至于几分钟内你就可以记住它。
RC4从创建一个256字节的数组&S&开始,并从0到255对其进行填充。然后从&S&的第0位开始循环,将&S&和密钥中的字节进行混合,这样做是为了创建用于产生&随机&数的状态机。为了生成随机数,我们将&S&数组进行洗牌(译注:参考百度百科RC4)
图形化描述是这样的:
对一个字节进行加密,我们对伪随机字节和要加密的字节进行异或运算。记住将一个比特和1进行异或的话是使这个比特反转(译注:0^1=1, 1^1=0)。因为前面产生的是随机数,所以大约会有一半的比特码会被反转,这种随机的比特反转在加密数据时非常有效。你已经看到,这并不复杂,而且运行起来很快。我想这也许就是Amazon用它的原因吧。
回想一下,我们有&client_write_key&和&server_write_key&。这意味这我们需要创建两个RC4实例,一个用于加密浏览器发送的消息,另一个用于解密服务器返回的消息。.
&client_write&最前面的随机字节是&E 20 7A 4D FE FB 78 A7 33 ...&,如果我们用这些字节和未加密的消息头(经查证,该消息的字节为&4 00 00 0C 98 F0 AE CB C4 ...&)进行异或运算的话,我们将得到在Wireshark中看到的已加密的部分:
服务器做的事情几乎一样。它发出一条&Change Cipher Spec&消息,然后发出的&Finished Message&消息,这条消息包括所有的&握手&消息,以及解密的客户端发过来的&Finished Message&,这也向客户端表明了服务端可以正确解密客户端发过来的消息。
欢迎回到应用层!
现在,220毫秒过去了,我们最终为应用层准备好了,现在我们可以发送通过TLS层使用RC4的写实例加密过的普通HTTP消息,也可以解密服务端RC4写实例发过来的消息。此外,TLS层还会通过计算消息内容的HMAC_MD5哈希值来校验每一条消息是否被篡改。
至此,&握手&过程已经完成,现在TLS记录的content type变成23(0x17)。加密的数据流以&17 03 01&开始,这几个字节表明了记录类型和TLS版本,后面是包含了HMAC哈希的加密数据。
对如下明文的加密:
GET /gp/cart/view.html/ref=pd_luc_mri HTTP/1.1
User-Agent: Mozilla/5.0 (W U; Windows NT 6.0; en-US;rv:1.9.0.10) Gecko/ Minefield/3.0.10 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/q=0.9,*/*;q=0.8
Accept-Language: en-us,q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive:300
Connection: keep-alive
将得到下面这些字节:
剩下的唯一有趣的事是每个消息的序列号是依次递增的,现在是1(下一个消息将会是2,以此类推)。
服务端做的事情是一样的,不过它使用&server_write_key&这个密钥。我们看到它的响应消息,包括泄露秘密的应用数据头。
这个消息解密后得到:
HTTP/1.1200 OK
Date: Wed, 10 Jun :30 GMT
Server: Server
Cneonction: close
Transfer-Encoding: chunked
这是一个普通的HTTP返回消息,它包含一个非描述性&Server: Server&HTTP头和一个来自Amazon负载均衡器的&Cneonction: close&头,显然拼写有误。
TLS就在应用层之下。HTTP服务器上的软件可以就像发送非加密数据一样工作,而唯一的区别是它们(指软件)将数据写到一个库,由这个库统一做加密。OpenSSL是TLS的一个非常流行的开源库。
在双方发送和接受加密数据的过程中,链接一直保持活动状态,直到任何一端发出一条&closure alert&消息并接着关闭连接。如果在断开后的很短时间内重连,则可以重用前面协商好的密钥(前提是服务器还缓存着他们),这样就不需要公钥操作,否则,就需要重新执行一次完全的握手过程。
应用数据消息可以是任何信息,这点很重要。&HTTPS&特别的唯一原因是因为Web太流行了,还有其他基于TCP/IP的协议运行在TLS之上。例如,TLS也用于FTPS和SMTP的安全扩展。使用TLS肯定比你自己发明解决方案要好,另外,使用经得起仔细的安全分析的协议,你能从中获益。
&&顺利完工!
TLS RFC中还包含很多我们这里遗漏的内容,它是一份可读性非常好的规范。我们覆盖的内容是对Firefox和Amazon之间的220毫秒的舞蹈进行观察所看到的一条路径。这个过程很大程度上受Amazon在ServerHello消息中所选择的TLS_RSA_WITH_RC4_128_MD5密码套件的影响,这是一个合理的选择,因为它对速度的倾向比对安全的倾向更多一点。
如我们所看到的,如果有人能将Amazon的模数&n&分解成&p&,&q&,那么他就可以有效解密所有的&安全&流通,直到Amazon换掉它的证书。所以Amazon通过每年替换一次证书的方式来平衡这方面的担心。
其中有一个密码套件是&TLS_DHE_RSA_WITH_AES_256_CBC_SHA&,它使用Diffie-Hellman密钥交换算法,这个算法具有良好的 &正向安全&的特点。也就意味这如果某人破解了密码交换的数学原理,对于解密下一个会话也没有什么益处。这个算法的缺点是需要计算更大的数字,所以对于一个很忙的服务器来说计算成本更大。&高级加密标准&(AES)出现在我们提供的很多密码套件中。它与RC4 的不同在于它一次加密16字节的&数据块&而不是一个字节,由于它的密钥可以大到256比特,所以很多人认为它比RC4要更安全。
仅需220毫秒中,网络中两个端点连接到一起,提供了足够资料来信任对方,建立加密算法,并开始进行加密的信息传输。

我要回帖

更多关于 openssl 生成随机数 的文章

 

随机推荐