随机数不需要保密。nonce 的优点是只出现一次,不会为外人所知。客户端随机数和服务器随机数以明文形式发送的事实并不能阻止它们成为“随机数”。
实际的“nonce”是客户端和服务器随机的串联。这在几个地方是明确的,特别是在RFC 5246 的第 6.3 节中,它处理密钥计算:用于加密的对称密钥和应用程序数据的 MAC 来自“主密钥”和两个随机数的连接:
key_block = PRF(SecurityParameters.master_secret,
"key expansion",
SecurityParameters.server_random +
SecurityParameters.client_random);
这两个随机值也将出现在CertificateVerify
和Finished
消息的计算中,因为它们都使用对所有先前握手消息计算的哈希值,其中包括两个“hello”消息和它们包含的随机值。
所以客户端随机通过发送一个新的来保护自己client_random
:无论在线上发生什么,客户端都会使用它发送的随机值进行密钥计算。同样,服务器使用其server_random
. 要了解保护如何工作,请考虑重放攻击:攻击者观察到客户端和服务器之间的握手,然后希望与服务器重放相同的握手,从而冒充客户端。攻击者观察到这一点:
ClientHello (ch1) --->
<--- ServerHello (sh1)
Certificate (c1)
ServerHelloDone (shd1)
ClientKeyExchange (cke1) --->
ChangeCipherSpec
Finished (cf1)
<--- ChangeCipherSpec
Finished (sf1)
然后攻击者连接到服务器,并发送ClientHello
(ch1)的精确副本。然而,服务器以不同 ServerHello
的响应(我们称之为 sh2):这ServerHello
与之前使用的(sh1)非常相似,但具有一个新server_random
值。然后攻击者可以继续握手,但是当攻击者发送Finished
消息 cf1 时事情就会崩溃:该消息应该包含从所有先前的握手消息派生的散列,并使用从主密钥派生的密钥进行加密(攻击者这样做不知道,他只是“重播”)以及来自客户端和服务器的随机数。对于这两种计算,服务器都期望Finished
加密的内容和密钥是使用随机来自ClientHello
和来自它自己的ServerHello
(即这里的“sh2”消息)。“cf1”消息将不匹配,因为它是使用来自初始握手的哈希和密钥计算的,来自 ch1 和 sh1 的随机数,而不是 sh2。
记录的数据包不能被攻击者全部重放,因为根据定义,攻击者只重放了一半的对话。当攻击者试图冒充客户端时,他可以代表客户端发送所有数据包,但必须使用真正的服务器响应(如果攻击者同时冒充客户端和服务器,那么他可以进行“完美重放”,但他实际上只是在自言自语,所以这被认为是一种精神疾病,而不是攻击)。server_random
从而强制执行阻止重放攻击的分歧。
(类似的情况是攻击者在与真正的客户端交谈时试图冒充服务器:client_random
保护客户端免受重放攻击。)
替代描述:客户端和服务器实际上确实验证了随机数。他们在计算对称密钥并检查接收到的Finished
消息的内容时会隐式执行此操作。客户端不会满足于Finished
从服务器接收到的消息,除非该消息是计算出来的,然后使用客户端刚刚发送的(以及其他值)加密client_random
,而不是其他值(对于服务器也是如此)。
客户端和服务器“随机”实际上不一定是随机的,尽管它是可取的。如上所述,客户端和服务器随机值中的实际值是它们与以前的值不同,对于每次握手。它们是公共价值观,如果它们是可预测的,就不会有任何严重的问题。事实上,即使攻击者可以预测给定客户端和服务器将使用的client_random
和/或的确切值server_random
,这也无助于他进行重放攻击:要成功重放握手,攻击者不能简单地知道客户端和服务器随机;他必须安排客户端和服务器重复使用与之前重播的握手相同的随机数。由于攻击者一次只能模拟一个,因此他无法强制重复使用。
您可能会注意到,上述 32 字节随机数实际上以 4 个非随机字节开始:这些字节编码当前日期和时间(自传统时刻以来的秒数)。这些字节的目的是尝试确保这种不重用策略,即使对于 PRNG 功能异常严重以至于它们重用以前的随机值的实现也是如此。但是,如果客户端没有好的随机源,这可能会影响ClientKeyExchange
主密钥和主密钥的安全性,尤其是基于 RSA 的密钥交换(这是司空见惯的)。