提示:英文原文写于2009年,当时的Firefox和最新版的Firefox,界面也有很大改动。如下是正文。
花了数小时阅读了如潮的好评,Bob最终火烧眉毛为他购买的托斯卡纳全脂牛奶点击了“进行结算”,而后……
哇!刚刚发生了什么?
在点击按钮事后的220毫秒时间内,发生了一系列有趣的事情,火狐浏览器(Firefox)不只改变了地址栏颜色,并且在浏览器的右下角出现了一个小锁头的标志。在我最喜欢的互联网工具Wireshark的帮助下,咱们能够经过一个通过略微调整的用于debug的火狐浏览器来探究这一过程。
根据RFC 2818标准(译者注:RFC 2818为HTTP Over TLS-网络协议),火狐浏览器自动经过链接Amazon.com的443端口来响应HTTPS请求。
不少人会把HTTPS和网景公司(Netscape)于上世纪九十年代中期建立的SSL(安全套接层)联系起来。事实上,随着时间的推移,这二者之间的关系也慢慢淡化。随着网景公司渐渐的失去市场份额,SSL的维护工做移交给了Internet工程任务组(IETF)。由网景公司发布的第一个版本被从新命名为TLS 1.0(安全传输层协议 1.0),并于1999年1月正式发布。考虑到TLS已经发布了将近10年,现在已经很难再见到真正的SSL通讯了。
客户端问候(Client Hello)
TLS将所有的通讯以不一样方式包裹为“记录”(Records)。咱们能够看到,从浏览器发出的第一个字节为0x16(十进制的22),它表示了这是一个“握手”记录。
接下来的两个字节是0x0301,它表示了这是一条版本为3.1的记录,同时也向咱们代表了TLS1.0其实是基于SSL3.1构建而来的。
整个握手记录被拆分为数条信息,其中第一条就是咱们的客户端问候(Client Hello),即0x01。在客户端问候中,有几个须要着重注意的地方:
- 随机数:
在客户端问候中,有四个字节以Unix时间格式记录了客户端的协调世界时间(UTC)。协调世界时间是从1970年1月1日开始到当前时刻所经历的秒数。在这个例子中,0x4a2f07ca就是协调世界时间。在他后面有28字节的随机数,在后面的过程当中咱们会用到这个随机数。
- SID(Session ID):
在这里,SID是一个空值(Null)。若是咱们在几秒钟以前就登录过了Amazon.com,咱们有可能会恢复以前的会话,从而避免一个完整的握手过程。
- 密文族(Cipher Suites):
密文族是浏览器所支持的加密算法的清单。整个密文族是由推荐的加密算法“TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA”和33种其余加密算法所组成。别担忧其余的加密算法会出现问题,咱们一下子就会发现Amazon也没有使用推荐的加密算法。
- Server_name扩展:
经过这种方式,咱们可以告诉Amazon.com:浏览器正在试图访问https://www.amazon.com。这确实方便了不少,由于咱们的TLS握手时间发生在HTTP通讯以前,而HTTP请求会包含一个“Host头”,从而使那些为了节约成本而将数百个网站域名解析到一个IP地址上的网络托管商可以分辨出一个网络请求对应的是哪一个网站。传统意义上的SSL一样要求一个网站请求对应一个IP地址,可是Server_name扩展则容许服务器对浏览器的请求授予相对应的证书。若是没有其余的请求,Server_name扩展应该容许浏览器访问这个IPV4地址一周左右的时间。
服务器问候(Server Hello)
Amazon.com回复的握手记录由两个比较大的包组成(2551字节)。记录中包含了0x0301的版本信息,意味着Amazon赞成咱们使用TLS1.0访问的请求。这条记录包含了三条有趣的子信息:
1.服务器问候信息(Server Hello)(2):
- 咱们获得了服务器的以Unix时间格式记录的UTC和28字节的随机数。
- 32字节的SID,在咱们想要从新链接到Amazon.com的时候能够避免一整套握手过程。
- 在咱们所提供的34个加密族中,Amazon挑选了“TLS_RSA_WITH_RC4_128_MD5”(0x0004)。这就意味着Amazon会使用RSA公钥加密算法来区分证书签名和交换密钥,经过RC4加密算法来加密数据,利用Md5来校验信息。咱们以后会深刻的研究这一部份内容。我我的认为,Amazon选择这一密码组是有其自身的缘由的。在咱们所提供的密码族中,这一加密组的加密方式是CPU占用最低的,这就容许Amazon的每台服务器接受更多的链接。固然了,也许还有一个缘由是,Amazon是在向这三种加密算法的发明者Ron Rivest(罗恩·李·维斯特)致敬。
2.证书信息(11):
这段巨大的信息共有2464字节,其证书容许客户端在Amazon服务器上进行认证。这个证书其实并无什么奇特之处,你能经过浏览器浏览它的大部份内容。
3.服务器问候结束信息(14):
这是一个零字节信息,用于告诉客户端整个“问候”过程已经结束,而且代表服务器不会再向客户端询问证书。
校验证书
此时,浏览器已经知道是否应该信任Amazon.com。在这个例子中,浏览器经过证书确认网站是否受信,它会检查 Amazon.com 的证书,而且确认当前的时间是在“最先时间”2008年8月26日以后,在“最晚时间”2009年8月27日以前。浏览器还会确认证书所携带的公共密钥已被受权用于交换密钥。
为何咱们要信任这个证书?
证书中所包含的签名是一串很是长的大端格式的数字:
任何人均可以向咱们发送这些字节,但咱们为何要信任这个签名?为了解释这个问题,咱们首先要回顾一些重要的数学知识:
RSA加密算法的基础介绍
人人经常会问,编程和数学之间有什么联系?证书就为数学在编程领域的应用提供了一个实际的例子。Amazon的服务器告诉咱们须要使用RSA算法来校验证书签名。什么又是RSA算法呢?RSA算法是由麻省理工(MIT)的Ron Rivest、Adi Shamirh和Len Adleman(RSA命名各取了三人名字中的首字母)三人于上世纪70年代建立的。三位天才的学者结合了2000多年数学史上的精华,发明了这种简洁高效的算法:
选取两个较大的初值p和q,相乘得n;n = p*q 接下来选取一个较小的数做为加密指数e,d做为解密指数是e的倒数。在加密的过程当中,n和e是公开信息,解密密钥d则是最高机密。至于p和q,你能够将他们公开,也能够做为机密保管。可是必定要记住,e和d是互为倒数的两个数。
假设你如今有一段信息M(转换成数字),将其加密只须要进行运算:C ≡ Me (mod n)
这个公式表示M的e次幂,mod n表示除以n取余数。当这段密文的接受者知道解密指数d的时候就能够将密文进行还原:Cd ≡ (Me)d ≡ Me*d ≡ M1 ≡ M (mod n)
有趣的是,解密指数d的持有者还能够将信息M进行用解密指数d进行加密:Md ≡ S (mod n)
加密者将S、M、e、n公开以后,任何人均可以得到这段信息的原文:Se ≡ (Md)e ≡ Md*e ≡ Me*d ≡ M1 ≡ M (mod n)
如同RSA的公共密钥加密算法常常被称之为非对称算法,由于加密密钥(在咱们的例子中为e)和解密密钥(在咱们的例子中是d)并不对称。取余运算的过程也不像咱们日常接触的运算(诸如对数运算)那样简单。RSA加密算法的神奇之处在于你能够很是快速的进行数据的加密运算,即 ,可是若是没有解密密码d,你将很难破解出密码,即运算 将不可能实现。正如咱们所看到的,经过对n进行因式分解而获得p和q,再推断出解密密钥d的过程难于上青天。
签名验证
在使用RSA加密算法的时候,最重要的一条就是要确保任何涉及到的数字都要足够复杂才能保证不被现有的计算方法所破解。这些数字要多复杂呢?Amazon.com的服务器是利用“VeriSign Class 3 Secure Server CA”来对证书进行签名的。从证书中,咱们能够看到这个VeriSign(电子签名校验器,也称威瑞信公司)的系数n有2048位二进制数构成,换算成十进制足足有617位数字:
1890572922 9464742433 9498401781 6528521078 8629616064 3051642608 4317020197 7241822595 6075980039 8371048211 4887504542 4200635317 0422636532 2091550579 0341204005 1169453804 7325464426 0479594122 4167270607 6731441028 3698615569 9947933786 3789783838 5829991518 1037601365 0218058341 7944190228 0926880299 3425241541 4300090021 1055372661 2125414429 9349272172 5333752665 6605550620 5558450610 3253786958 8361121949 2417723618 5199653627 5260212221 0847786057 9342235500 9443918198 9038906234 1550747726 8041766919 1500918876 1961879460 3091993360 6376719337 6644159792 1249204891 7079005527 7689341573 9395596650 5484628101 0469658502 1566385762 0175231997 6268718746 7514321
(若是你想要对这一大串数字进行分解因式得到p和q,那就祝你好运!顺便一提,若是你真的计算出了p和q,那你就破解了Amazon.com数字签名证书了!)
这个VeriSign的加密密钥e是 。固然,他们将解密密钥d保管得十分严密,一般是在拥有视网膜扫描和荷枪实弹的警卫守护的机房当中。在签名以前,VeriSign会根据相关约定的技术文档,对Amazon.com证书上所提供的信息进行校验。一旦证书信息符合相关要求,VeriSign会利用SHA-1哈希算法获取证书的哈希值(hash),并对其进行声明。在Wireshark中,完整的证书信息会显示在“signedCertificate”(已签名证书)中:
这里应该是软件的用词不当,由于这一段其实是指那些即将被签名的信息,而不是指那些已经包含了签名的信息。
实际上通过签名的信息S,在Wireshark中被称之为“encrypted”(密文)。咱们将S的e次幂除以n取余数(即公式: )就能计算出被加密的原文,其十六进制以下:
0001FFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFF00302130 0906052B0E03021A05000414C19F8786 871775C60EFE0542 E4C2167C830539DB
根据PKCS#1 v1.5标准(译者注:The Public-Key Cryptography Standards (PKCS)是由美国RSA数据安全公司及其合做伙伴制定的一组公钥密码学标准)规定:“第一个字节是00,这样就能够保证加密块在被转换为整数的时候比其加密参数要小。”第二个字节为01,表示了这是一个私有密钥操做(数字签名就是私有密钥操做的一种)。后面紧接着的一连串的FF字节是为了填充数据,使得这一串数字变得足够大(加大黑客恶意破解的难度)。填充数字以一个00字节结束。紧接着的30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14这些字节是PKCS#1 v2.1标准中用于说明这段哈希值是经过SHA-1算法计算而出的。最后的20字节是SHA-1算法所计算出来的哈希值,即对未加密信息的摘要描述。(译者注:原文中这里使用了带引号的signedCertificate,根据做者前文描述,这应该是Wireshark软件的bug,实际上应指的是未被加密的信息。)
由于这段信息的格式正确,且最后的哈希值与咱们独立计算出来的校验一致,因此咱们能够判定,这必定是知道“VeriSign Class 3 Secure Server CA”的解密密钥d的人对它进行了签名。而世界上只有VeriSign公司才知道这串密钥。
固然了,咱们也能够重复验证这个“VeriSign Class 3 Secure Server CA”的证书的确是经过VeriSign公司的“第三类公私证书认证(Class 3 Public Primary Certification Authority)”进行签名的。
可是,即使是这样,咱们为何要信任VeriSign公司?整个的信任链条就此断掉了。
由图能够看到,“VeriSign Class 3 Secure Server CA”对Amazon.com进行了签名,而“VeriSign Class 3 Public Primary Certification Authority”对“VeriSign Class 3 Secure Server CA”进行了签名,可是最顶部的“VeriSign Class 3 Public Primary Certification Authority”则对本身进行了签名。这是由于,这个证书自从NSS(网络安全服务)库中的certdata.txt 升级到1.4版以后就做为“受信任的根证书颁发机构”(译者注:参照微软官方翻译)被编译到了Mozilla产品中(火狐浏览器)。这段信息是由网景公司的Robert Relyea于2000年9月6日提交的,并随附如下注释:
“由仅存的NSS编译了框架。包含一个在线的certdata.txt文档,其中包含了咱们受信的跟证书颁发机构(一旦咱们得到了其余受信机构的许可会陆续将他们添加进去)”。
这个举动有着至关长远的影响,由于这些证书的有效日期是从1996年1月28日到2028年1月1日。
肯·汤普逊(Ken Thompson)在他的《对深信不疑的信任》(译者注:Reflections on Trusting Trust是肯汤普逊1983年得到图灵奖时的演说)的演说中解释的很好:你最终仍是要绝对信任某一人,在这个问题上没有第二条路可走。在本文的例子中,咱们就毫无保留的信任Robert Relyea作了一个正确的决定。咱们一样但愿Mozilla在本身软件中加入“受信任根证书颁发机构”这种行为也是合理的吧。
这里须要注意的是:这一系列的证书和签名只是用来造成一个信任链。在公共互联网上,VeriSign的根证书被火狐浏览器彻底信任的时间远早于你接触互联网。在一个公司中,你能够建立本身的受信任的根证书颁发机构并把它安装到任何人的计算机中。
相对的,你也能够购买VeriSign公司的业务,下降整个证书信任链的信任风险。经过第三方的认证机构(在这个例子里是VeriSign公司)咱们能利用证书创建起信任关系。若是你有相似于“悄悄话”的安全途径来传递一个秘密的key,那你也能够使用一个预共享密钥(PSK)来创建起信任关系。诸如TLS-PSK、或者带有安全远程密码(SRP)的TLS扩展包都能让咱们使用预共享密钥。不行的是,这些扩展包在应用和支持方面远远比不上TLS,因此他们有的时候并不实用。另外,这些替代选项须要额外德尔安全途径进行保密信息的传输,这一部分的开销远比咱们如今正在应用的TLS庞大。换句话说,这也就是咱们为何不该用那些其余途径构建信任关系的缘由。
言归正传,咱们所须要的最后确认的信息就是在证书上的主机名跟咱们预想的是同样的。Nelson Bolyard在SSL_AuthCertificate 函数中的注释为咱们解释其中的缘由:
“SSL链接的客户端确认证书正确,并检查证书中所对应的主机名是否正确,由于这是咱们应对中间人攻击的惟一方式!” (译者注:中间人攻击是一种“间接”的入侵攻击,这种攻击模式是经过各类技术手段将受入侵者控制的一台计算机虚拟放置在网络链接中的两台通讯计算机之间,这台计算机就称为“中间人”。)
1
2
3
|
/* cert is OK. This is the client side of an SSL connection.
* Now check the name field in the cert against the desired hostname.
* NB: This is our only defense against Man-In-The-Middle (MITM) attacks! */
|
这样的检查是为了防止中间人攻击:由于咱们对整个信任链条上的人都采起了彻底信任的态度,认为他们并不会进行黑客行为,就像咱们的证书中所声称它是来自Amazon.com,可是假如他的真实来源并不是Amazon.com,那咱们可能就有被攻击的危险。若是攻击者使用域名污染(DNS cache poisoning)等技术对你的DNS服务器进行篡改,那么你也许会把黑客的网站误认为是一个安全的受信网站(诸如Amazon.com),由于地址栏显示的信息一切正常。这最后一步对证书颁发机构的检查就是为了防止这样的事情发生。
随机密码串(Pre-Master Secret)
如今咱们已经了解了Amazon.com的各项要求,而且知道了公共解密密钥e和参数n。在通讯过程当中的任何一方也都知道了这些信息(佐证就是咱们经过Wireshark得到了这些信息)。如今咱们所须要作的事情就是生成一串窃密者/攻击者都不能知道的随机密码。这并不像听上去的那么简单。早在1996年,研究人员就发现了网景浏览器1.1的伪随机数发生器仅仅利用了三个参数:当天的时间,进程ID和父进程ID。正如研究人员所指出的问题:这些用于生成随机数的参数并不具备随机性,并且他们相对来讲比较容易被破解。
由于一切都是来源于这三个随机数参数,因此在1996,利用当时的机器仅须要25秒钟的时间就能够破解一个SSL通讯。找到一种生成真正随机数的方法是很是困难的,若是你不相信这一点,那就去问问Debian OpenSSL的维护工程师吧。若是随机数的生成方式遭到破解,那么创建在这之上的一系列安全措施都是毫无心义的。
在Windows操做系统中,用于加密目的随机数都是利用一个叫作CryptGenRandom的函数生成的。这个函数的哈希表位对超过125个来源的数据进行抽样!火狐浏览器利用CryptGenRandom函数和它自身的函数来构成它本身的伪随机数发生器。(译者注:之因此称之为伪随机数是由于真正意义上的随机数算法并不存在,这些函数仍是利用大量的时变、量变参数来经过复杂的运算生成相对意义上的随机数,可是这些数之间仍是存在统计学规律的,只是想要找到生成随机数的过程并不那么容易)。
咱们并不会直接利用生成的这48字节的随机密码串,可是因为不少重要的信息都是由他计算而来的,因此对随机密码串的保密就显得格外重要。正如我以前所预料到的,火狐浏览器对随机密码串的保密十分严格,因此我不得不编译了一个用于debug的版本。为了观察随机密码串,我还特意设置了SSLDEBUGFILE和SSLTRACE两个环境变量。
其中,SSLDEBUGFILE显示的就是随机密码串的值:
1
2
3
4
|
4456:SSL[131491792]:Pre-MasterSecret[Len:48]
0301bb7b0898a749dee8e9b89152ec81...{...I.....R..
4cc2397bf6ba1c0ab1955029be02ade6L.9{......P)....
ad6e113f20c466f06422577ee1067a3b.n.?.f.d"W~..z;
|
须要注意的是,这串数字从各类意义上来讲都不是真正的随机数,就拿它的前两位来讲:这就是根据TLS协议约定的TLS版本号(0301)。
密码交换(Trading Secret)
咱们如今须要作的就是计算出Amazon.com所要求的密码。由于Amazon.com但愿使用“TLS_RSA_WITH_RC4_128_MD5”加密组,因此咱们使用RSA加密算法进行这一过程。你能够将这48字节的随机密码串做为初始参数,可是根据公共密钥密码标准(PKCS)#1 v1.5中的注释,咱们须要用随机数据将随机密码串填充到实际要求的参数大小(1024位二进制/128字节)。这样的话攻击者想要破解咱们的随机密码串就难上加难了。这也是咱们保障本身安全的最后一道防线,以防咱们在前面的步骤中犯了诸如重复使用密码这样的低级错误。若是咱们重复使用了随机密码串,因为使用了随机数填充,窃密者在网络中拦截的也会是两个不一样的值。
一样的,咱们很难直接观察到火狐浏览器中的这一过程,因此我不得不在填充随机数的函数中增长了debug的语句,使咱们可以观察这一过程:
1
2
3
4
5
6
7
8
|
wrapperHandle = fopen("plaintextpadding.txt", "a");
fprintf(wrapperHandle, "PLAINTEXT = ");
for(i = 0; i < modulusLen; i++)
{
fprintf(wrapperHandle, "%02X ", block[i]);
}
fprintf(wrapperHandle, "\r\n");
fclose(wrapperHandle);
|
在这个例子中,完整的填充后的随机密码串为:
00 02 12 A3 EA B1 65 D6 81 6C 13 14 13 62 10 53 23 B3 96 85 FF 24 FA CC 46 11 21 24 A4 81 EA 30 63 95 D4 DC BF 9C CC D0 2E DD 5A A6 41 6A 4E 82 65 7D 70 7D 50 09 17 CD 10 55 97 B9 C1 A1 84 F2 A9 AB EA 7D F4 CC 54 E4 64 6E 3A E5 91 A0 06 00 03 01 BB 7B 08 98 A7 49 DE E8 E9 B8 91 52 EC 81 4C C2 39 7B F6 BA 1C0A B1 95 50 29 BE 02 AD E6 AD 6E 11 3F20 C4 66 F0 64 22 57 7E E1 06 7A 3B
火狐浏览器使用这个值计算出 ,咱们能够看到它显示在“客户端交换密钥”(Client Key Exchange)的记录中:
在这个过程的最后,火狐浏览器会发送一个不加密的信息:一条“Change Cipher Spec”记录:
经过这种方式:火狐浏览器要求Amazon.com在后面的通讯过程当中使用约定的加密方式传输信息。
得到主密钥(Master Secret)
若是咱们正确完成了以前的过程,而且各方都得到了48字节(256二进制位)的随机密码串。从Amazon.com的角度来看,这里还有一些信任问题:随机密码串是由客户端生成的,并无将任何服务器信息或者以前约定的信息加入其中。这一点,咱们会经过生成主密钥的方式加以完善。根据协议规范约定,这个的计算过程为:
1
|
master_secret=PRF(pre_master_secret,"master secret",ClientHello.random+ServerHello.random)
|
pre_master_secret就是咱们以前传送的随机密码串,”master secret”是一串ASCII码(例如:6d 61 73 74 65 72……),再链接上在客户端问候和服务器问候(来自Amazon的)的信息。
PRF是在规范中约定的伪随机函数,它将密钥、ASCII码标签、哈希值整合在一块儿。各有一半的参数分别使用MD5和SHA-1获取哈希值。这是一种十分明智的作法,即便是想要单单破解相对简单MD5和SHA-1也不是那么容易的事情。并且这个函数会将返回值传给自身直至迭代到咱们须要的位数。
利用这个函数,咱们生成了48字节的主密钥:
4C AF 20 30 8F4C AA C5 66 4A 02 90 F2 AC 10 00 39 DB 1D E0 1F CB E0 E0 9D D7 E6 BE 62 A4 6C 18 06 AD 79 21 DB 82 1D 53 84 DB 35 A7 1F C1 01 19
生成各类密钥
如今,各方面已经有了主密钥,根据协议约定,咱们须要利用PRF生成这个会话中所须要的各类密钥,称之为“密钥块”(key block):
1
|
key_block = PRF(SecurityParameters.master_secret, "key expansion", SecurityParameters.server_random + SecurityParameters.client_random);
|
密钥块用于构成如下密钥:
1
2
3
4
5
6
|
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)的密码流代替了分组密码咱们就不须要初始化向量(IVs)了。所以咱们只须要双方的两个16字节(128二进制位)的消息认证码(Message Authentication Code,MAC),由于MD5的哈希值就是16字节的。此外,双方也须要16字节(128二进制位)的RC4码。因此咱们总共须要从密码块得到2*16 + 2*16 = 64字节的数据。
运行PRF,咱们能获得如下值:
1
2
3
4
|
client_write_MAC_secret = 80 B8 F6 09 51 74 EA DB 29 28 EF 6F 9A B8 81 B0
server_write_MAC_secret = 67 7C 96 7B 70 C5 BC 62 9D 1D 1F 4A A6 79 81 61
client_write_key = 32 13 2C DD 1B 39 36 40 84 4A DE E5 6C 52 46 72
server_write_key = 58 36 C4 0D 8C 7C 74 DA 6D B7 34 0A 91 B6 8F A7
|
准备加密!
客户端最后一次送出的握手信息是“结束信息”。这条信息保证了没有人篡改握手信息,而且咱们已经知晓所必须的密钥。客户端将整个握手过程的所有信息都放入一个名为“handshake_messages”的缓冲区。咱们能经过伪随机函数利用主密钥、“client finished”标签、MD5和SHA-1的哈希值生成12字节的“区别数据”(verify_data):
1
|
verify_data=PRF(master_secret,"client finished",MD5(handshake_messages)+SHA-1(handshake_messages))[12]
|
咱们在这个结果前面加上0x14(用于表示结束信息)和00 00 0c(用于表示verify_data 有12字节)。就像之后全部的加密过程同样,咱们要在加密以前确保原始数据没有被篡改。由于咱们使用的是“TLS_RSA_WITH_RC4_128_MD5”密码组,这就意味着咱们须要使用MD5哈希函数。
有些人一听到MD5函数就会嗤之以鼻,由于其自身的确存在一些缺陷。我本身固然也不会推荐这种算法。可是TLS的聪明之处就在于他并不直接使用MD5函数,只是利用哈希值的版原本校验数据。只就意味着咱们并未直接应用到MD5(m):
HMAC_MD5(Key, m) = MD5((Key ⊕ opad) ++ MD5((Key ⊕ ipad) ++ m)
(其中,⊕表示的是异或运算)
在实际中:
HMAC_MD5(client_write_MAC_secret, seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length + TLSCompressed.fragment));
正如你所见,咱们在函数中使用了一个根据明文(在这里明文叫作“TLSCompressed”)编号的序号(seq_num)。这个序号的做用就是为了阻止攻击者在数据流中间插入以前被其截获的信息。若是发生了这样的攻击,序号就能清楚的警告咱们数据中的异常。一样的,这个序号也能帮助咱们发现攻击者从数据流中剔除的数据。
剩下的工做只剩下啊加密这些数据了!
RC4加密
咱们以前协商过的密码组是“TLS_RSA_WITH_RC4_128_MD5”。这就意味着咱们须要使用RC4(Ron`s code 4)加密规则进行数据流的加密。罗纳德李威斯特(Ron Rivest)开发了这种基于一个256字节的Key产生随机加密效果的算法。这个算法简单到你几分钟就能记住。
首先,RC4生成一个256字节的数组S,并用0-255填充。接下来的工做就是将须要将KEY混合插入进数组中并反复迭代。你能够编写一个状态机,利用它来阐释随机字节。为了产生随机字节,咱们须要将S数组打乱,如图所示:
为了加密一个字节,咱们将其与随机字节进行异或运算。记住:一位二进制数与1做异或运算会翻转(译者注:即1^1=0;1^0=1)。由于咱们利用的是随机数,因此从统计学的角度来说,有一半的数被翻转。这种随机翻转的现象就是咱们加密数据的有效方法。正如你所见,这并不复杂,并且计算速度十分快,我认为这也是Amazon.com选择它的缘由之一。
记得咱们有“client_write_key”和“server_write_key”吗?这就意味咱们须要两个RC4实例:一个用来加密浏览器向服务器传送的数据,一个用来解密服务器向浏览器传送的数据。
“client_write_key”最初的几个字节是7E 20 7A 4D FE FB 78 A7 33 …若是咱们对这些字节和未加密的数据头以及版本信息(“14 00 00 0C98 F0 AE CB C4 …”)进行异或运算,咱们就能获得在Wireshark中看到的加密信息了:
服务器端作的几乎是相同的事情。它们发送了一个密钥协议的说明和一个包含所有握手过程的结束信息,其中有结束信息的解密版本。所以,这种机制就保证了客户端和服务器能成功的解密信息。
欢迎来到应用层!
如今,从咱们点击了按钮以后已通过去了220毫秒,咱们终于为应用层作好了准备!如今,咱们发送的普通的HTTP数据流会经过TLS层的加密实例进行加密,在服务器的解密实例进行解密。并且TLS会对数据进行哈希校验,以保证数据内容的准确性。
在这个时候,整个的握手过程就结束了。咱们的TLS记录内容如今有了23条(0x17)。加密数据以“17 03 01”开头,表示了记录类型和TLS版本,后面紧跟着加密数据的大小和哈希校验值。
加密的数据的明文以下:
1
2
3
4
5
6
7
8
9
10
|
GET /gp/cart/view.html/ref=pd_luc_mri HTTP/1.1
Host: www.amazon.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.10) Gecko/2009060911 Minefield/3.0.10 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;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
...
|
在Wireshark中显示以下:
惟一有趣的地方是序号是按照记录来增加,这条记录是1,下一条就是2。
服务器端利用“server_write_key”作着一样的事情。咱们能看到服务器的相应结果,包括程序开头的指示位:
解密后的信息以下:
1
2
3
4
5
6
|
HTTP/1.1200OK
Date:Wed,10Jun200901:09:30GMT
Server:Server
...
Cneonction:close
Transfer-Encoding:chunked
|
这就是一个来自Amazon负载平衡服务器的普通HTTP回应:包含了非描述性的服务器信息“Server: Server”和一个拼错了的“Cneonction: close”。
TLS层在应用层的下面,因此软件和服务器可以像正常的HTTP传输那样进行工做,惟一的区别就是传输的数据会被TLS层进行加密。
OpenSSL是一个应用很广的TLS开源库。
整个链接会一直保持,除非有一方提出了“关闭警告(closure alert)”而且关闭了链接。若是咱们在链接断开后的短期内再次提出链接请求,咱们能够使用以前使用过的key来进行链接,从而避免一次新的握手过程。(这个要取决于服务器端key的有效时间。)
须要注意的是:应用程序能够发送任何数据,可是HTTPS的特殊之处在于WEB应用的普遍普及。要知道还有很是多的基于TCP/IP而且使用TLS进行数据加密的协议(如FTPS,sSMTP)。使用TLS要比你本身发明一种是数据加密方案便捷的多。何况,你所使用的安全协议必定要足够安全。
…完工!
TLS RFC的文档包含了更多的信息,有须要的朋友们能够本身查阅,咱们在这里只是简单的介绍了其中的过程和原理,观察了这220毫秒内发生在火狐浏览器和Amazon服务器之间发生的故事:由Amazon.com基于速度和安全的综合考虑选择的“TLS_RSA_WITH_RC4_128_MD5”密码组在HTTPS链接创建过程当中的所有流程。
正如咱们所看到的那样,若是有人能对Amazon服务器的参数n进行因式分解获得p和q的话,那他就能破解所有的基于亚马逊证书的安全通讯。因此Amazon为这个参数设置了有效期以防止这种事情的发生:
在咱们提供的密码族中,有一组密码组“TLS_DHE_RSA_WITH_AES_256_CBC_SHA”使用了Diffie-Hellman密钥交换,并所以能提供良好的前向安全特性。这就意味着若是有人破解了交换密钥的数学运算方式,他们也不能利用这个来破解其余的会话。可是他的一个劣势在于其运算需求更大的数字和更高的运算能力。AES算法在不少密码组中都出现了,它与RC4的不一样之处在于它每次使用的是16字节的“块”而RC4使用的是单字节。由于其key最高能到256位二进制位,因此通常认为它比RC4的安全性更高。
在短短的220毫秒的时间里,两个节点经过互联网链接起来,而且利用一系列手段创建起了互信机制,构建了加密算法,进行加密数据的传输。
正是由于如此,咱们故事的主人公才能在Amazon上买到他想要的牛奶!