tls是什么
为什么要有HTTPS?
因为HTTP不安全。
由于HTTP天生具有“明文”的特定,整个传输过程完全透明,任何人都能在链路中截获、修改或者伪造请求/响应报文,数据不具备可信性。
比如说,“代理服务”作为HTTP通信的中间人,在数据上下行的时候,可以添加或者删除部分头字段,也可以使用黑白名单过滤body里的关键词,甚至直接发送虚假的请求、响应,而浏览器和源服务器都没有办法判断报文的真伪。
这对于网络购物、网上银行等需要高度信任的场景来说是非常致命的。
什么是安全
既然HTTP不安全,那什么样的通信过程才是安全的呢?
通常认为,如果通信过程具备了机密性、完整性、身份认证、不可否认这四个特性,就可以认为是安全的。
- 机密性:是指对数据“保密”,只能由可信的人访问,对其他人是不可见的,简单的来说就是不能让不相关的人看到不该看的东西
- 完整性(也就是一致性):是指数据在传输过程中没有被篡改,不多也不少,“完整”的保存着原状
- 身份认证:保证消息只能发给可信的人
- 不可否认:也叫不可抵赖,意思是不能否认已经发生过的行为,不能“说话不算数”“耍赖皮”。
什么是HTTPS
HTTPS为HTTP增加了刚刚说的四大安全特性。
HTTPS其实是一个“非常简单”的协议,RFC文档很小,只有短短7页,里面规定了新的协议名叫做https,默认端口号是443,置于其他的什么请求-应答模式、报文结构、请求方法等都完全沿用HTTP,没有任何新的东西。
也就是说,除了协议名“http”的端口号80这两点不同,HTTPS协议在语法上、语义上HTTP完全一样,优缺点也“照单全收”(当然要除去“明文”和“不安全”)
使用实验环境https://www.chrono.com
访问时chrome会有不安全提示,必须点击“高级-继续前往”才能顺序显示页面。而且如果使用wireshark’抓包,也会发现与HTTP不一样,不再是简单可见的明文,多了“client hello”,“server hello”等新的数据包。
这就是HTTPS和HTTP最大的区别,它能够鉴别危险的网站,并且尽最大可能的保证你的上网安全。
既然没有新东西,HTTPS凭什么能够做到机密性、安全性等这些特性呢?
机密就在HTTPS中的“S”,它把HTTP下层的传输协议由TCP/IP换成了SSL/TLS,由HTTP over TCP/IP
变成了HTTP over SSL/TLS
,让HTTP运行在安全的SSL/TLS协议上,收发报文不再使用Socket API,而是调用专门的安全接口。
SSL/TLS
SSL即安全套接层(Secure Sockets Layer),在OSI模型中出于第五层(会话层),由网景公司于1994年发明,有v2和v3两个版本,而v1因为有着严重的缺陷从没有公开过。
SSL发展到v3的时候已经证明了它自身是一个非常好的安全通信协议,于是互联网工厂组IETF在1999年的时候把它改名为TLS(传输层安全,Transport Layer Security),正式标准化,版本号从1.0重新算起,所以TLS1.0实际上就是SSLv3.1.
到今天为止TLS已经发展出了三个版本,分别是2006年的1.1、2008年的1.2和2018年的1.3,每个新版本都紧跟密码学的发展和互联网的现状,持续强化安全和性能,已经成为了信息安全领域中的权威标准
密切应用最广泛的TSL是1.2,之前的协议(TLS1.1/1.0,SSLv3/v2)都已经被认为是不安全的,各大浏览器在2020年左右停止支持了。
TLS由记录协议,握手协议,警告协议,变更密码规范协议,扩展协议等几个子协议组成,综合使用了对称密码、非对称加密、身份认证等很多密码学技术。
浏览器和服务器在使用TLS建立连接时需要选择一组恰当的加密算法来实现安全通信,这些算法的组合叫做“密码套件”(ciper suite,也叫做加密套件)
比如上面协商选定的是"ECDHE-RSA-AES256-GCM-SHA384”。
- 基本的形式是“密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法”,
- 比如刚才的密码套件的意思就是:“握手时使用 ECDHE 算法进行密钥交换,用 RSA 签名和身份认证,握手后的通信使用 AES 对称算法,密钥长度 256 位,分组模式是 GCM,摘要算法 SHA384 用于消息认证和产生随机数。”
OpenSSL
说到 TLS,就不能不谈到 OpenSSL,它是一个著名的开源密码学程序库和工具包,几乎支持所有公开的加密算法和协议,已经成为了事实上的标准,许多应用软件都会使用它作为底层库来实现 TLS 功能,包括常用的 Web 服务器 Apache、Nginx 等。
- OpenSSL 是从另一个开源库 SSLeay 发展出来的,曾经考虑命名为 OpenTLS,但当时(1998 年)TLS 还未正式确立,而 SSL 早已广为人知,所以最终使用了 OpenSSL 的名字。
- OpenSSL 目前有三个主要的分支,1.0.2 和 1.1.0 都将在今年(2019)年底不再维护,最新的长期支持版本是 1.1.1,我们的实验环境使用的 OpenSSL 是 1.1.0j。
- 由于 OpenSSL 是开源的,所以它还有一些代码分支,比如 Google 的 BoringSSL、OpenBSD 的 LibreSSL,这些分支在 OpenSSL 的基础上删除了一些老旧代码,也增加了一些新特性,虽然背后有 大金主,但离取代 OpenSSL 还差得很远。
小结
- 因为HTTP是明文传输,所以不安全,容易被黑客窃听或者篡改
- 通信安全必须同时具备机密性、完整性、身份验证和不可否认这四个特性
- HTTPS的语法、语义仍然是HTTP,但是把下层的协议由TCP/IP换成了SSL/TLS
- SSL/TLS是信息安全领域中的权威标准,采用多种先进的加密技术来保证通信安全
- OpenSSL是著名的开源加密学工具包,是SSL/TLS的具体实现
如何实现机密性:加密
实现机密性最常用的手段就是加密,就是把消息用某种方式转换成谁也看不懂的乱码,只有掌握特殊“钥匙”的人才能转换出原始文本。
-
这里的钥匙就叫做密钥,加密前的消息叫做明文,加密后的乱码叫做密文,使用密钥还原明文的过程叫做解密,加密解密的过程就是加密算法。
-
所有的加密算法都是公开的,任何人都可以去分开研究,而算法使用的密钥则必须保密,那么,这个密钥又是什么呢?
由于HTTPS、TLS都运行在计算机上,所以“密钥”就是一长串对的数字,但约定俗成的度量单位是bit,而不是byte,比如密钥长度是128,就是16byte的二进制串。
按照密钥的使用方式,加密可以分为两大类:对称加密和非对称加密
’
对称加密
对称加密就是加密和解密时使用的密钥都是同一个,是“对称”的。只要保证了密钥的安全,那整个通信过程就有了机密性
TLS 里有非常多的对称加密算法可供选择,比如 RC4、DES、3DES、AES、ChaCha20 等,但前三种算法都被认为是不安全的,通常都禁止使用,目前常用的只有 AES 和 ChaCha20。
-
AES 的意思是“高级加密标准”(Advanced Encryption Standard),密钥长度可以是 128、192 或 256。它是 DES 算法的替代者,安全强度很高,性能也很好,而且有的硬件还会做特殊优化,所以非常流行,是应用最广泛的对称加密算法。
-
ChaCha20 是 Google 设计的另一种加密算法,密钥长度固定为 256 位,纯软件运行性能要超过 AES,曾经在移动客户端上比较流行,但 ARMv8 之后也加入了 AES 硬件优化,所以现在不再具有明显的优势,但仍然算得上是一个不错的算法。
非对称加密
对称加密看上去好像完美地实现了机密性,但其中有一个很大的问题:如何把密钥安全地传递给对方,术语叫“密钥交换”。
-
因为在对称加密算法中只要持有密钥就可以解密。如果你和网站约定的密钥在传递途中被黑客窃取,那他就可以在之后随意解密收发的数据,通信过程也就没有机密性可言了。这个问题该怎么解决呢?
-
你或许会说:“把密钥再加密一下发过去就好了”,但传输“加密密钥的密钥”又成了新问题。这就像是“鸡生蛋、蛋生鸡”,可以无限递归下去。只用对称加密算法,是绝对无法解决密钥交换的问题的。
于是,就出现了非对称加密,也就是公钥加密算法。
-
它有两个密钥,一个叫做公钥(public key),一个叫做私钥(private key)。两个密钥是不同的,“不对称”,公钥可以公开,私钥必须严格保密。
-
公钥和私钥都有个特别的“单向”性,虽然都可以用来加密解密,但是公钥加密之后只能用私钥解密,反过来,私钥加密之后也只能用公钥解密。
非对称加密可以解决密钥交换问题。网站秘密保管私钥,在网上任意分发公钥,你想要登录网站只要用公钥加密就行了,密文只能由私钥持有者才能解密。而黑客因为没有私钥,所以就无法破解密文。
非对称加密算法的设计要比对称算法难得多,在 TLS 里只有很少的几种,比如 DH、DSA、RSA、ECC 等。
RSA 可能是其中最著名的一个,几乎可以说是非对称加密的代名词,它的安全性基于“整数分解”的数学难题,使用两个超大素数的乘积作为生成密钥的材料,想要从公钥推算出私钥是非常困难的。
10 年前 RSA 密钥的推荐长度是 1024,但随着计算机运算能力的提高,现在 1024 已经不安全,普遍认为至少要 2048 位。
ECC(Elliptic Curve Cryptography)是非对称加密里的“后起之秀”,它基于“椭圆曲线离散对数”的数学难题,使用特定的曲线方程和基点生成公钥和私钥,子算法 ECDHE (实际上就是非对称密钥的交换算法,具体见4.2.2)用于密钥交换,ECDSA (本文中未出现)用于数字签名。
混合加密
看到这里,你是不是认为可以抛弃对称加密,只用非对称加密来实现机密性呢?
很遗憾,虽然非对称加密没有“密钥交换”的问题,但因为它们都是基于复杂的数学难题,运算速度很慢,如果仅用非对称加密,虽然保证了安全,但通信速度有如乌龟、蜗牛,实用性就变成了零。
那么,是不是能够把对称加密和非对称加密结合起来呢,两者互相取长补短,即能高效地加密解密,又能安全地密钥交换。这就是现在 TLS 里使用的混合加密方式,其实说穿了也很简单:
- 在通信刚开始的时候使用非对称算法,比如 RSA、ECDHE,首先解决密钥交换的问题。
- 然后用随机数产生对称算法使用的“会话密钥”(session key),再用非对称算法公钥加密。因为会话密钥很短,通常只有 16 字节或 32 字节,所以慢一点也无所谓。对方拿到密文后用非对称算法私钥解密,取出会话密钥。
- 这样,双方就通过非对称算法实现了对称密钥的安全交换(用非对称算法保证了会话秘钥的安全),后续就不再使用非对称加密,全都使用对称加密。
如何实现完整性:摘要算法
实现完整性的手段主要是摘要算法(digest algorithm),也就是常说的散列函数、哈希函数。
不过摘要算法不具有机密性,如果明文传输,那么黑客可以修改消息后把摘要也一起改了,网站还是鉴别不出完整性。所以,真正的完整性必须要建立在机密性之上,在混合加密系统里用会话密钥加密消息和摘要,这样黑客无法得知明文,也就没有办法动手脚了。这有个术语,叫哈希消息认证码(HMAC)。
- 你可以把摘要算法理解成一种特殊的压缩函数,它能够把任意长度的数据“压缩”成固定长度(输入任意长度的数据,输出固定长度的数据),而且独一无二的“摘要”字符串,也就是给这段数据生成了一个“数字指纹”
- 摘要算法实际上是把数据从一个“大空间”映射到了“小空间”,所以就存在“冲突”(collision,也叫碰撞)的可能性,就如同现实中的指纹一样,可能会有两份不同的原文对应相同的摘要。好的摘要算法必须能够“抵抗冲突”,让这种可能性尽量地小。
- 比如,你发了条消息:“转账 1000 元”,然后再加上一个 SHA-2 的摘要。网站收到后也计算一下消息的摘要,把这两份“指纹”做个对比,如果一致,就说明消息是完整可信的,没有被修改。
- 如果黑客在中间哪怕改动了一个标点符号,摘要也会完全不同,网站计算比对就会发现消息被窜改,是不可信的。
- 摘要算法保证了“数字摘要”和原文是完全等价的。所以,我们只要在原文后附上它的摘要,就能够保证数据的完整性。
- 它的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密,目前可以被解密逆向的只有CRC32算法,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文
- 消息摘要算法不存在密钥的管理与分发问题,适合于分布式网络上使用。由于其加密计算的工作量相当巨大,所以以前的这种算法通常只用于数据量有限的情况下的加密
- 摘要算法可以看成是单向加密算法,它只有算法,没有密钥,加密后的数字无法解密,不能从摘要逆推出原文
- 因为摘要算法对输入具有“单向性”和“雪崩效应”,输入的微小不同都会导致输出的剧烈变化,所以也别TLS用来生成伪随机数
摘要算法有哪些?
- MD5(Message-Digest 5)、SHA-1(Secure Hash Algorithm 1)是最常用的两个摘要算法,能够生成 16 字节和 20 字节长度的数字摘要。但这两个算法的安全强度比较低,不够安全,在 TLS 里已经被禁止使用了。
- 目前 TLS 推荐使用的是 SHA-1 的后继者:SHA-2。SHA-2 实际上是一系列摘要算法的统称,总共有 6 种,常用的有 SHA224、SHA256、SHA384,分别能够生成 28 字节、32 字节、48 字节的摘要。
消息摘要算法是有关于数字签名的核心算法,下面讲解MD5的一个应用:HTTP中的摘要认证机制。
1、HTTP摘要认证主要通过4个方法保证用户的认证安全和隐私:
- (1)通过传递用户名,密码等计算出来的摘要来解决明文方式在网络上发送密码的问题。
- (2)通过服务产生随机数nonce的方式可以防止恶意用户捕获并重放认证的握手过程。
- (3)通过客户端产生随机数cnonce的方式,支持客户端对服务器的认证。
- (4)通过对内容也加入摘要计算的方式,可以有选择的防止对报文内容的篡改
2、摘要认证的握手过程
(1)客户端请求
GET /dir/index.html HTTP/1.0
Host: localhost
(2)服务器响应
- 服务器产生一个随机数nonce
- 服务器将这个随机数放在WWW-Authenticate响应头,与服务器支持的认证算法列表,认证的域realm一起发送给客户端,如下例子:
HTTP/1.0 401 Unauthorized
Server: HTTPd/0.9
Date: Sun, 10 Apr 2005 20:26:47 GMT
WWW-Authenticate: Digest realm="testrealm@host.com",
qop="auth ",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
Content-Type: text/html
(3)客户端请求 (用户名 “Mufasa”, 密码 “Circle Of Life”)
- 客户端发现是401响应,表示需要进行认证,则弹出让用户输入用户名和密码的认证窗口
- 客户端采用MD5计算出密码和其他数据的摘要,将摘要放到Authorization的请求头respons中发送给服务器
- 如果客户端要对服务器也进行认证,这个时候,可以发送客户端随机数cnonce。如下例子:
GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
(4)服务器响应
- 服务接受摘要,通过MD5算法以及服务端保存的密码等数据,重新计算新的摘要跟客户端传输的摘要进行比较,验证是否匹配
- 若客户端反过来用客户端随机数对服务器进行质询,就会创建客户端摘要,服务可以预先将下一个随机数计算出来,提前传递给客户端,通过Authentication-Info发送下一个随机数。如下例子:
HTTP/1.1 200 OK
Authorization-Info:nextnonce=” 88C4EF58DA7CB956BD04233FBB64E0A4”
qop=”auth”
rspauth=”23543534DfasetwerwgDTerGDTERERRE”
cnonce=” xxxxx234132543strwerr65sgdrftdfytryts”
3.客户端生成的摘要信息如何得到?
response 值由三步计算而成。当多个数值合并的时候,使用冒号作为分割符,摘要计算的信息块主要有两种:
(1) 表示与安全相关的数据:对用户名、认证域(realm)以及密码的合并值计算 MD5 哈希值,结果称为
HA1 = MD5(<UserName>:<realm>:<Password>)
(2) 表示与报文相关的数据: 对HTTP方法以及URI的摘要的合并值计算 MD5 哈希值,主要目的是有助于防止反复,资源或者报文被篡改,结果称为
HA2 = MD5(<request-method>:<uri-directive-value>)
例如:“GET” 和 “/dir/index.html”
(3) 对 HA1、服务器密码随机数(nonce)、请求计数(nc)、客户端密码随机数(cnonce)、保护质量(qop)以及 HA2 的合并值计算 MD5 哈希值。结果即为客户端提供的 response 值。
HD=nonce:noncecount:cnonce:qop;
Response = MD5(HA1:HD:HA2)
如何进行身份验证:数字签名
加密算法结合摘要算法,这样就保证了完整性,我们的通信过程可以说是比较安全了。但这里还有漏洞,就是通信的两个端点(endpoint)。
就像一开始所说的,黑客可以伪装成网站来窃取信息。而反过来,他也可以伪装成你,向网站发送支付、转账等消息,网站没有办法确认你的身份,钱可能就这么被偷走了。
现实生活中,解决身份认证的手段是签名和印章,只要在纸上写下签名或者盖个章,就能够证明这份文件确实是由本人而不是其他人发出的。
你回想一下之前的课程,在 TLS 里有什么东西和现实中的签名、印章很像,只能由本人持有,而其他任何人都不会有呢?只要用这个东西,就能够在数字世界里证明你的身份。
没错,这个东西就是非对称加密里的“私钥”,使用私钥再加上摘要算法,就能够实现“数字签名”,同时实现“身份认证”和“不可否认”。
- 数字签名的原理其实很简单,就是把公钥私钥的用法反过来,之前是公钥加密、私钥解密,现在是私钥加密、公钥解密。
- 但又因为非对称加密效率太低,所以私钥只加密原文的摘要,这样运算量就小的多,而且得到的数字签名也很小,方便保管和传输。
- 签名和公钥一样完全公开,任何人都可以获取。但这个签名只有用私钥对应的公钥才能解开,拿到摘要后,再比对原文验证完整性,就可以像签署文件一样证明消息确实是你发的。
刚才的这两个行为也有专用术语,叫做“签名”和“验签”。
只要你和网站互相交换公钥,就可以用“签名”和“验签”来确认消息的真实性,因为私钥保密,黑客不能伪造签名,就能够保证通信双方的身份。
比如,你用自己的私钥签名一个消息“我是小明”。网站收到后用你的公钥验签,确认身份没问题,于是也用它的私钥签名消息“我是某宝”。你收到后再用它的公钥验一下,也没问题,这样你和网站就都知道对方不是假冒的,后面就可以用混合加密进行安全通信了。实验环境的 URI“/25-2”演示了 TLS 里的数字签名,它使用的是 RSA1024。
数字签名和CA
到现在,综合使用对称加密、非对称加密和摘要算法,我们已经实现了安全的四大特性,是不是已经完美了呢?
不是的,这里还有一个“公钥的信任”问题。因为谁都可以发布公钥,我们还缺少防止黑客伪造公钥的手段,也就是说,怎么来判断这个公钥就是你或者某宝的公钥呢?
真是“按下葫芦又起了瓢”,安全还真是个麻烦事啊,“一环套一环”的。我们可以用类似密钥交换的方法来解决公钥认证问题,用别的私钥来给公钥签名,显然,这又会陷入“无穷递归”。但这次实在是“没招”了,要终结这个“死循环”,就必须引入“外力”,找一个公认的可信第三方,让它作为“信任的起点,递归的终点”,构建起公钥的信任链。
- 这个“第三方”就是我们常说的 CA(Certificate Authority,证书认证机构)。它就像网络世界里的公安局、教育部、公证中心,具有极高的可信度,由它来给各个公钥签名,用自身的信誉来保证公钥无法伪造,是可信的。
- CA 对公钥的签名认证也是有格式的,不是简单地把公钥绑定在持有者身份上就完事了,还要包含序列号、用途、颁发者、有效时间等等,把这些打成一个包再签名,完整地证明公钥关联的各种信息,形成“数字证书”(Certificate)。
- 知名的 CA 全世界就那么几家,比如 DigiCert、VeriSign、Entrust、Let’s Encrypt 等,它们签发的证书分 DV、OV、EV 三种,区别在于可信程度。
DV 是最低的,只是域名级别的可信,背后是谁不知道。EV 是最高的,经过了法律和审计的严格核查,可以证明网站拥有者的身份(在浏览器地址栏会显示出公司的名字,例如 Apple、GitHub 的网站)。
不过,CA 怎么证明自己呢?这还是信任链的问题。小一点的 CA 可以让大 CA 签名认证,但链条的最后,也就是 Root CA,就只能自己证明自己了,这个就叫“自签名证书”(Self-Signed Certificate)或者“根证书”(Root Certificate)。你必须相信,否则整个证书信任链就走不下去了。
有了这个证书体系,操作系统和浏览器都内置了各大 CA 的根证书,上网的时候只要服务器发过来它的证书,就可以验证证书里的签名,顺着证书链(Certificate Chain)一层层地验证,直到找到根证书,就能够确定证书是可信的,从而里面的公钥也是可信的。
我们的实验环境里使用的证书是“野路子”的自签名证书(在 Linux 上用 OpenSSL 命令行签发),肯定是不会被浏览器所信任的,所以用 Chrome 访问时就会显示成红色,标记为不安全。但你只要把它安装进系统的根证书存储区里,让它作为信任链的根,就不会再有危险警告。