TLS1.2的密钥计算

本文详细解释了TLS1.2和1.3中的关键概念,包括预主密钥、主密钥、会话密钥的生成过程,以及HMAC和伪随机函数的作用。特别介绍了TLS1.3中密钥管理和HKDF算法的改变。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. TLS 1.2 中的密钥

在 TLS 1.2 中,有 3 种密钥:预主密钥主密钥会话密钥(密钥块),这几个密钥都是有联系的。

1. 预主密钥 PreMasterSecret

  • 对于 RSA 握手协商算法来说,Client 会生成的一个 48 字节的预主密钥,其中前 2 个字节是 ProtocolVersion,后 46 字节是随机数,用 Server 的公钥(服务器公钥证书certificate)加密之后通过 Client Key Exchange 子消息发给 Server,Server 用私钥来解密。

     struct {
         uint32 gmt_unix_time;
         opaque random_bytes[28];
     } Random;
     
    struct {
         ProtocolVersion client_version;
         opaque random[46];
     } PreMasterSecret;  
    
    struct {
         uint8 major;
         uint8 minor;
     } ProtocolVersion;       
    
  • 对于 (EC)DHE握手协商算法来说,预主密钥是双方通过椭圆曲线算法生成的,双方各自生成临时公私钥对,保留私钥,将公钥发给对方,然后就可以用自己的私钥以及对方的公钥通过椭圆曲线算法来生成预主密钥,预主密钥长度取决于 DH/ECDH 算法公钥。预主密钥长度是 48 字节或者 X 字节

2. 主密钥

主密钥是由「预主密钥、ClientHello random 和 ServerHello random 」通过 PRF 函数生成的。主密钥长度是 48 字节

只要我们知道预主密钥或者主密钥便可以解密抓包数据,所以 TLS 1.2 中抓包解密调试只需要一个主密钥即可,SSLKEYLOG 就是将主密钥导出来,在 Wireshark 里面导入就可以解密相应的抓包数据。

3. 会话密钥(密钥块)

会话密钥(密钥块)是由「主密钥、SecurityParameters.server_random 和 SecurityParameters.client_random 」数通过 PRF 函数来生成。

会话密钥里面包含对称加密密钥、消息认证和 CBC 模式的初始化向量,对于非 CBC 模式的加密算法来说,就没有用到这个初始化向量。

4. 会话复用

Session ID 缓存和 Session Ticket 里面保存的也是主密钥,而不是会话密钥,这样每次会话复用的时候再用双方的随机数和主密钥导出会话密钥,从而实现每次加密通信的会话密钥不一样,即使一个会话的主密钥泄露了或者被破解了也不会影响到另一个会话。

二. TLS 1.2 中的 HMAC 和伪随机函数

1. HMAC

HMAC:基于哈希函数和密钥的消息认证码。在TLS握手过程中,HMAC用于计算和验证握手消息的MAC值,验证消息的完整性和认证。

2. 伪随机函数PRF

为了进行密钥生成或验证,需要一个 MAC 算法对数据块进行扩展以增加机密性。这个伪随机函数(PRF)将 secret,seed 和 label 作为输入,产生任意长度的输出。

在 TLS 1.2 中,基于 HMAC 定义了一个 PRF 函数。这个使用 SHA-256 hash 函数的 PRF 函数被用于所有的密码算法套件。

首先,我们定义一个数据扩展函数 P_hash(secret, data),它是PRF中的一个子函数,用于生成伪随机数据。它使用一个 hash 函数扩展成一个 secret 和种子,形成任意大小的输出:

P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                       HMAC_hash(secret, A(2) + seed) +
                       HMAC_hash(secret, A(3) + seed) + ...
// 这里"+"是指级联        

A(0) = seed
A(i) = HMAC_hash(secret, A(i-1))              

必要时 P_hash 可以被多次迭代,以产生所需数量的数据。例如,如果 P_SHA256 被用于产生 80 字节的数据,它应该被迭代 3 次(通过 A(3)),SHA_256 每次输出 32 字节(256 bit),迭代 3 次才能产生 96 字节的输出数据,最终迭代产生的最后 16 字节会被丢弃,留下 80 字节作为输出数据。

TLS 的 PRF 可以通过将 P_hash 运用与 secret 来实现:

PRF(secret, label, seed) = P_<hash>(secret, label + seed)
// 将label + seed 视为 seed

其中,label 是一个 ASCII 字符串。它应该以严格地按照它被给出的内容转化为十六进制格式。

三. TLS 1.2 中的密钥计算

1. 计算主密钥

为了开始连接保护,TLS 记录协议要求指定一个算法套件,一个主密钥和 Client 及 Server 端随机数。认证,加密和消息认证码算法由 cipher_suite 确定,cipher_suite 是由 Server 选定并在 ServerHello 消息中表明出来的。压缩算法在 hello 消息里协商出来,随机数也在 hello 消息中交换。所有这些都用于计算主密钥。

对于所有的密钥交换算法,相同的算法都会被用来将 pre_master_secret 转化为 master_secret。一旦 master_secret 计算完毕,pre_master_secret就应当从内存中删除。避免攻击者获取预备主密钥,如果攻击者获取到了预主密钥,加上 ClientHello.random 和 ServerHello.random 传输过程中是不加密的,也容易获取,那么攻击者就可以合成主密钥并进一步导出会话密钥,这样整个加密过程就被完全破解了。

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

主密钥的长度一直是 48 字节。预主密钥的长度根据密钥交换算法而变。

  • RSA

    当RSA被用于身份认证和密钥交换时,Client 会产生一个 48 字节的 pre_master_secret,用 Server 的公钥加密,然后发送给 Server。Server 用它自己的私钥解密 pre_master_secret。然后双方按照前述方法将 pre_master_secret转换为 master_secret。

  • Diffie-Hellman

    一个传统的 Diffie-Hellman 计算需要被执行。协商出来的密钥(Z)会被用做pre_master_secret,并按照前述方法将其转换为 master_secret。在被用做pre_master_secret之前,Z 开头所有的 0 位都会被压缩。

    注:Diffie-Hellman 参数由 Server 指定,可能是临时的也可能包含在 Server 的证书中。

2. 计算增强型主密钥(开启EMS)

如果 ClientHello 的扩展中携带了 extended_master_secret 扩展,表示 Client 和 Server 使用增强型主密钥计算方式。

Server 在 ServerHello 中响应该扩展,返回了一个的 extended_master_secret 扩展,表明会使用增强型主密钥计算方式。

那么增强型主密钥是如何计算的呢?计算方式如下:

master_secret = PRF(pre_master_secret, "extended master secret",
                    session_hash)
                    [0..47];

上面的计算方式和普通计算主密钥方式不同点在于:

  • “extended master secret” 替代了 “master secret”
  • session_hash 替代了 ClientHello.random + ServerHello.random

“session_hash” 的计算取决于多个因素,包括密码套件、密钥交换信息、证书(如果有的话)、“ClientHello.random” 和 “ServerHello.random” 。

3. 计算会话密钥

主密钥被扩张为一个安全字节序列,它被分割为四项:为client_write_MAC_keyserver_write_MAC_keyclient_write_keyserver_write_key。它们都是从字节序列中以上述顺序生成。未使用的值是空。

一些AEAD加密可能会额外需要一个 client_write_IV 和一个 server_write_IV

因此,会话密钥(密钥块)的长度和个数取决于协商出来的密码套件,更准确的说是取决于加密参数 SecurityParameters,需要使用 PRF 函数扩展出足够长的密钥块,计算如下:

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

注意:计算会话密钥和主密钥使用 PRF 的三个入参都不同,PRF(secret, label, seed)

  • 主密钥是 (pre_master_secret, "master secret", ClientHello.random + ServerHello.random)

  • 会话密钥是 (SecurityParameters.master_secret, "key expansion", SecurityParameters.server_random + SecurityParameters.client_random)

    seed 顺序有变化,Client 和 Server 随机数的组合顺序会调换。

直到产生足够的输出。然后,key_block会按照如下方式分开:

client_write_MAC_key[SecurityParameters.mac_key_length]
server_write_MAC_key[SecurityParameters.mac_key_length]
client_write_key[SecurityParameters.enc_key_length]
server_write_key[SecurityParameters.enc_key_length]
client_write_IV[SecurityParameters.fixed_iv_length]
server_write_IV[SecurityParameters.fixed_iv_length]

client_write_keyserver_write_keyclient_write_MAC_keyserver_write_MAC_key加密消息验证码需要的密钥。Client 和 Server 分别拥有自己的一套密钥,使用的密钥是不同的。

如果是分组加密方式,还需要初始化向量 client_write_IVserver_write_IV。如果是 AEAD 模式,client_write_MAC_keyserver_write_MAC_key 可以不需要,使用 client_write_IVserver_write_IV 作为随机值。

一些用到的结构体如下:

enum { null(0), (255) } CompressionMethod;

enum { server, client } ConnectionEnd;

enum { tls_prf_sha256 } PRFAlgorithm;

enum { null, rc4, 3des, aes } BulkCipherAlgorithm;

enum { stream, block, aead } CipherType;

enum { null, hmac_md5, hmac_sha1, hmac_sha256, hmac_sha384,
  hmac_sha512} MACAlgorithm;

/* Other values may be added to the algorithms specified in
CompressionMethod, PRFAlgorithm, BulkCipherAlgorithm, and
MACAlgorithm. */

struct {
    ConnectionEnd          entity; // 连接端点,可以是server或client
    PRFAlgorithm           prf_algorithm; // 目前仅支持tls_prf_sha256
    BulkCipherAlgorithm    bulk_cipher_algorithm; // 批量加密算法
    CipherType             cipher_type; // 加密算法类型
    uint8                  enc_key_length; // 加密密钥的长度
    uint8                  block_length; // 加密算法的块长度
    uint8                  fixed_iv_length; // 固定初始化向量(IV)的长度
    uint8                  record_iv_length; // 记录级别的初始化向量(IV)的长度
    MACAlgorithm           mac_algorithm; // MAC算法
    uint8                  mac_length; // MAC值的长度
    uint8                  mac_key_length; // MAC密钥的长度
    CompressionMethod      compression_algorithm; // 压缩算法
    opaque                 master_secret[48];
    opaque                 client_random[32];
    opaque                 server_random[32];
} SecurityParameters;

4. 总结

在这里插入图片描述

四. TLS 1.2 Finished 校验

在 TLS 1.2 握手的最后,会发送 Finished 子消息,这条消息是加密的第一条消息,Finished 消息的接收者必须要验证这条消息的内容是否正确。验证的内容是通过 PRF 算法计算出来的。

verify_data = PRF(master_secret, 
					finished_label, 
					Hash(handshake_messages))
     				[0..verify_data_length-1];

1. finished_label

  • Client:finished_label = "client finished"
  • Server:finished_label = "server finished",seed 是所有握手消息的 hash 值。

2. handshake_messages

  • Client:包含所有发送的消息和接收的消息,但是不包括自己发送的 Finished 消息。
  • Server:包含从 ClientHello 消息开始截止到 Finished 消息之前的所有消息,也包括 Client 的 Finished 子消息。

注意:handshake_messages 中只包含握手子消息,不包括 ChangeCipherSpec 子消息、 Alert 子消息、HelloRequest 消息。

3. verify_data 长度

早期 TLS 协议,verify_data 的长度是 12 字节,对于 TLS 1.2 协议来说,verify_data 的长度取决于密钥套件,如果密码套件没有指定 verify_data_length,则默认长度也是 12 字节。

五. TLS 1.3 中的密钥

在 TLS 1.3 中,不再使用 PRF 这种算法了,而是采用更标准的 HKDF 算法来进行密钥推导。而且在 TLS 1.3 中对密钥进行了更细粒度的优化,每个阶段或者方向的加密都不是使用同一个密钥。

TLS 1.3 在 ServerHello 消息之后的数据都是加密的,握手期间 Server 给 Client 发送的消息用 server_handshake_traffic_secret 通过 HKDF 算法导出的密钥加密的,Client 发送给 Server 的握手消息是用 client_handshake_traffic_secret 通过 HKDF 算法导出的密钥加密的。

这两个密钥是通过 Handshake Secret 密钥来导出的,而 Handshake Secret 密钥又是由 PreMasterSecret 和 Early Secret 密钥导出,然后通过 Handshake Secret 密钥导出主密钥 Master Secret。

在这里插入图片描述

参考资料

  1. HTTPS 温故知新(五) —— TLS 中的密钥计算
<think>好的,我现在需要解决用户的问题:如何使用HMAC-SHA512结合TLS会话密钥和物理熵源来生成摇奖种子。首先,我得理解每个部分的作用以及它们如何结合起来。 首先,用户提到的HMAC-SHA512是一种基于哈希的消息认证码算法,使用SHA-512哈希函数。HMAC需要密钥和消息来生成认证码,这里用户希望将TLS会话密钥作为HMAC的密钥,而物理熵源作为消息输入。这样生成的HMAC输出就可以作为摇奖种子,确保其随机性和不可预测性。 接下来,我需要确认TLS会话密钥的用途。在TLS握手过程中,客户端和服务器协商生成会话密钥,用于加密通信。这个密钥是保密的,且具有足够的随机性,适合作为HMAC的密钥部分。因为会话密钥是每次会话唯一的,这增加了摇奖种子的唯一性。 然后是物理熵源。物理熵源通常指来自硬件设备的随机数,比如噪声、温度变化等,这些数据具有高熵值,难以预测。将物理熵源作为HMAC的消息输入,可以确保消息部分的随机性。不过需要考虑物理熵源的数据格式,可能需要先进行预处理,比如哈希或者编码,确保其适合作为HMAC的输入。 接下来是具体步骤。首先,需要获取TLS会话密钥,这通常在TLS握手完成后由库或框架提供,可能需要调用特定的API来获取。然后,收集物理熵源的数据,可能需要使用硬件接口或者系统提供的随机数生成器。例如,在Linux系统中,可以从/dev/random或/dev/urandom读取熵数据。 然后是使用HMAC-SHA512算法进行处理。将TLS会话密钥作为密钥,物理熵源的数据作为消息输入,生成HMAC值。这个结果就是摇奖种子,长度应该是512位(64字节)。需要确保在这个过程中,密钥和消息的处理是正确的,特别是密钥可能需要处理成适当的长度,如果密钥过长可能需要哈希,过短可能需要填充,但HMAC本身有处理密钥的机制,所以可能不需要额外处理。 最后,需要考虑安全性。使用HMAC-SHA512可以提供抗碰撞和抗篡改的特性,但必须保证TLS会话密钥的保密性,如果密钥泄露,可能会影响摇奖种子的安全性。同时,物理熵源的质量也很重要,需要确保其足够的随机性,否则可能被预测。 可能还需要考虑实现的具体语言和库。例如,在Python中可以使用hmac和hashlib库,Java中使用javax.crypto包,或者PHP中的hash_hmac函数。需要根据具体环境选择正确的实现方式,并测试生成的种子是否符合预期。 此外,摇奖种子的应用场景可能要求其不可预测和公正,因此需要公开可验证的方法,比如在生成后使用哈希函数再次处理,或者结合多个熵源来增强安全性。同时,可能需要将生成的种子进行编码(如Base64或十六进制)以便存储或传输。 总结步骤大致如下: 1. 获取TLS会话密钥作为HMAC密钥2. 收集物理熵源数据作为HMAC的消息。 3. 使用HMAC-SHA512算法处理,生成摇奖种子。 4. 对生成的种子进行编码和存储。 需要验证每个步骤的正确性,并确保各部分的来源安全可靠。同时,引用相关的资料,比如HMAC的工作原理和TLS会话密钥的生成过程,来支持方法的可行性。</think>### HMAC-SHA512结合TLS会话密钥与物理熵源生成摇奖种子的方法 #### 1. **核心组件说明** - **TLS会话密钥**:在TLS握手过程中生成的对称密钥(如AES密钥),具备唯一性和高随机性[^1]。可通过调用TLS库的API获取(如OpenSSL的`SSL_get_session_key`)。 - **物理熵源**:来自硬件设备的随机数据(如热噪声、光电效应),通过系统接口读取(如Linux的`/dev/random`或专用硬件模块)。 - **HMAC-SHA512**:以密钥TLS会话密钥)和消息(物理熵源)为输入,输出固定长度的认证码(64字节),确保数据完整性和真实性[^2]。 #### 2. **实现步骤** **步骤1:获取TLS会话密钥** ```python # 示例:通过Python的ssl库获取TLS会话密钥(需实际库支持) import ssl context = ssl.create_default_context() with socket.create_connection(("example.com", 443)) as sock: with context.wrap_socket(sock, server_hostname="example.com") as ssock: tls_key = ssock.session.keys() # 假设存在此API(实际需适配具体库) ``` **步骤2:收集物理熵源数据** ```python # 从物理熵源设备读取数据(示例为Linux系统) with open("/dev/random", "rb") as f: entropy_data = f.read(64) # 读取64字节物理熵 ``` **步骤3:生成HMAC-SHA512摇奖种子** ```python import hmac import hashlib # 使用TLS密钥作为HMAC密钥,物理熵作为消息 hmac_seed = hmac.new(tls_key, entropy_data, hashlib.sha512).digest() ``` **步骤4:编码与验证** ```python # 将二进制种子转为十六进制字符串 seed_hex = hmac_seed.hex() print(f"摇奖种子: {seed_hex}") ``` #### 3. **安全增强措施** - **密钥隔离**:确保TLS会话密钥仅在内存中使用,不持久化存储。 - **多熵源混合**:可结合多个物理熵源(如温度传感器、鼠标移动事件)提升随机性。 - **后处理哈希**:对HMAC输出再次使用SHA-512哈希,防止侧信道攻击。 #### 4. **数学原理** HMAC-SHA512计算公式为: $$ \text{HMAC}(K, M) = \text{SHA512}\left( (K \oplus \text{opad}) \parallel \text{SHA512}\left( (K \oplus \text{ipad}) \parallel M \right) \right) $$ 其中: - $K$为TLS会话密钥(补零或截断为块大小) - $M$为物理熵源数据 - $\text{opad}$和$\text{ipad}$为固定填充常量 #### 5. **应用场景示例** - **彩票系统**:在开奖时生成不可预测的种子,确保公平性。 - **区块链随机数**:为智能合约提供去中心化随机源。 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值