在 PSK TLS 中,用于加密的密钥是如何派生的?

信息安全 加密 tls 解密
2021-09-06 02:12:46

我正在尝试使用TLS_PSK_WITH_3DES_EDE_CBC_SHA. 我现在有了客户端随机数、服务器随机数、PSK 标识和 PSK 值。我的问题是如何派生用于加密和解密的密钥。

我已阅读 RFC 2246 TLS 1.0 和 RFC 4279 Pre-Shared Key Ciphersuites for TLS 标准。我的理解是,在客户端和服务器之间的 PSK TLS 握手期间,客户端和服务器就使用哪个 PSK(预共享密钥)达成一致。此 PSK 将用于派生会话密钥。此会话密钥用于消息的加密和解密。然而,在我阅读这些标准的过程中,我找不到一个公式或程序来得出这个结论。我找到了一个计算预主密钥的公式,但我不确定这是用于加密还是解密的公式。

  1. 在 PSK TLS 中,pre-master secret 是否与用于加密和解密的密钥相同?
  2. 如果预共享密钥与加解密所使用的密钥不同,那么这个密钥是如何导出的呢?
1个回答

预主密钥在 TLS 握手中被加密和交换。此值用于派生主密钥。主密钥用于通过将其传递给伪随机函数 (PRF) 并组合来自握手的其他数据片段来派生所有其他密钥材料。


导出 PSK 的 Pre-Master Secret

来自RFC427:第 2 节

预主密钥的形成如下:如果 PSK 的长度为 N 个八位字节,则将 auint16与 N 值、N 个零八位字节、auint16与 N 值以及 PSK 本身连接起来。

如果使用带 PSK 的 Diffie Hellman,第 3 节

预主密钥形成如下。首先,以与 [TLS] 中其他基于 Diffie-Hellman 的密码套件相同的方式执行 Diffie-Hellman 计算。令 Z 为该计算产生的值(与其他基于 Diffie-Hellman 的密码套件一样,前导零字节被剥离)。连接uint16包含 Z 长度(以八位字节为单位)的 a、Z 本身、uint16包含 PSK 长度(以八位字节为单位)的 a 以及 PSK 本身。

如果使用带 PSK 的 RSA,第 4 节

从客户端发送到服务器的 EncryptedPreMasterSecret 字段包含一个 2 字节的版本号和一个 46 字节的随机值,使用服务器的 RSA 公钥加密,如 [TLS] 的第 7.4.7.1 节所述。实际的 premaster secret 由双方形成如下:将 uint16 与值 48、2 字节版本号和 46 字节随机值、包含 PSK 长度(以八位字节为单位)和 PSK 的 uint16 连接起来本身。(因此,预主密钥比 PSK 长 52 个八位字节。)


派生主密钥

以下所有内容不仅适用于 TLS PSK;它适用于所有密钥交换类型。来自 RFC 2246:第 8.1 节

对于所有密钥交换方法,使用相同的算法将 pre_master_secret 转换为 master_secret。计算完 master_secret 后,应该从内存中删除 pre_master_secret。主密钥的长度始终为 48 字节。premaster secret 的长度将根据密钥交换方法而有所不同。

   master_secret = PRF(pre_master_secret, "master secret",
                       ClientHello.random + ServerHello.random)
   [0..47];

PRF 被定义为结合两个不同的散列函数。 RFC 2246 的第 5 节:

首先,我们定义一个数据扩展函数 P_hash(secret, data),它使用单个哈希函数将密钥和种子扩展为任意数量的输出:

   P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                          HMAC_hash(secret, A(2) + seed) +
                          HMAC_hash(secret, A(3) + seed) + ...

   Where + indicates concatenation.

   A() is defined as:
       A(0) = seed
       A(i) = HMAC_hash(secret, A(i-1))

上面描述的函数真的是P_MD5or P_SHA1TLS PRF 描述如下:

TLS 的 PRF 是通过将秘密分成两半并使用一半以 P_MD5 生成数据,另一半以 P_SHA-1 生成数据,然后将这两个扩展函数的输出进行异或 (XOR) 来创建的。

因此,让我们声明以下内容:

L_S = length in bytes of secret;
L_S1 = L_S2 = ceil(L_S / 2);

那么PRF是:

PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
                              P_SHA-1(S2, label + seed);

派生加密密钥

密钥派生在 RFC 2246:第 6.3 节中描述

整个keyblock派生如下:

key_block = PRF(SecurityParameters.master_secret,
                "key expansion",
                SecurityParameters.server_random +
                SecurityParameters.client_random);

一旦生成了足够的材料并将其存储在 中key_blockkey_block就会将其拆分为加密/解密密钥。加密或解密取决于方向。因为它们都被称为键。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]  

对可导出的分组密码执行了一些额外的计算。您也可以在 RFC2246 的第 6.3 节中阅读这些详细信息。 第 6.3.1 节给出了这些计算的示例,没有额外的描述。