16 03 03 00 50 41 54 6e f1 7a 2a 3f 1d 0f 26 29 95 7f ba 0a f2 d8 4b 4a 9a c5 b3 b4 71 2e ef 70 fa 39 17 5e cc 90 9f 33 16 c2 e3 ad 4a 3c 95 f1 fe 90 cd cc a1 98 d8 7c 0a b4 d6 50 3a fa 6a 16 f5 6e ff 06 77 a7 f4 66 db d2 fb b4 be cf 8d 7b c8 7c 78 78 71
where "handshake_messages" refers to all handshake messages sent or received, starting at the ClientHello up to and including the ClientKeyExchange message, including the type and length fields of the handshake messages. This is the concatenation of all the exchanged Handshake structures, as defined in Section 7.4 of [RFC5246].
For TLS 1.2, the "Hash" function is the one defined in Section 7.4.9 of [RFC5246] for the Finished message computation. For all previous versions of TLS, the "Hash" function computes the concatenation of MD5 and SHA1.
The premaster secret is formed as follows: if the PSK is N octets long, concatenate a uint16 with the value N, N zero octets, a second uint16 with the value N, and the PSK itself.
The above four cipher suites are the same as the corresponding cipher suites in RFC 4279 and RFC 4785 (with names ending in "_SHA" in place of "_SHA256" or "_SHA384"), except for the hash and PRF algorithms, as explained below.
o For cipher suites with names ending in "_SHA256":
* The MAC is HMAC [RFC2104] with SHA-256 as the hash function.
* When negotiated in a version of TLS prior to 1.2, the PRF from that version is used; otherwise, the PRF is the TLS PRF [RFC5246] with SHA-256 as the hash function.
key_block = PRF(SecurityParameters.master_secret,
"key expansion",
SecurityParameters.server_random +
SecurityParameters.client_random);
until enough output has been generated. Then, the key_block is partitioned as follows:
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]
struct {
opaque verify_data[verify_data_length];
} Finished;
verify_data
PRF(master_secret, finished_label, Hash(handshake_messages))
[0..verify_data_length-1];
finished_label
For Finished messages sent by the client, the string "client finished". For Finished messages sent by the server, the string "server finished".
Any cipher suite which does not explicitly specify verify_data_length has a verify_data_length equal to 12
the Hash MUST be the Hash used as the basis for the PRF
All of the data from all messages in this handshake (not including any HelloRequest messages) up to, but not including, this message. This is only data visible at the handshake layer and does not include record layer headers.
Once the use of encrypt-then-MAC has been negotiated, processing of TLS/DTLS packets switches from the standard:
encrypt( data || MAC || pad )
to the new:
encrypt( data || pad ) || MAC
with the MAC covering the entire packet up to the start of the MAC value.
for TLS 1.1 and greater with an explicit IV is:
MAC(MAC_write_key, seq_num +
TLSCipherText.type +
TLSCipherText.version +
TLSCipherText.length +
IV +
ENC(content + padding + padding_length));
The overall TLS packet is then:
struct {
ContentType type;
ProtocolVersion version;
uint16 length;
GenericBlockCipher fragment;
opaque MAC;
} TLSCiphertext;
sequence number
Each connection state contains a sequence number, which is maintained separately for read and write states. The sequence number MUST be set to zero whenever a connection state is made the active state. Sequence numbers are of type uint64 and may not exceed 2^64-1. Sequence numbers do not wrap. If a TLS implementation would need to wrap a sequence number, it must renegotiate instead. A sequence number is incremented after each record: specifically, the first record transmitted under a particular connection state MUST use sequence number 0.
个人理解简单来说就是一个8字节的值,初始值为0,client及server针对接收和发送消息都维护自己的两个sequence number,每次接收消息后read sequence number + 1,每次发送消息后write sequence number+1
本文通过一个演示例子讲解基于PSK( Pre-Shared Key,预共享密钥)的TLS(Transport Layer Security)协议是如何加密及计算MAC的,对于想要实现TLS-PSK Server及client的读者应该有所帮助。
在演示例子中,客户端与服务端使用TLS1.2及PSK-AES128-CBC-SHA256密码套件进行交互,客户端发送"hi server\n",服务端响应"hi client\n",然后正常终止连接
准备工作
运行tcpdump,将后续捕获到的TLS数据保存到文件
抓取本地4433端口流量并保存到tls_psk.pcap文件
使用openssl client与openssl server建立TLS-PSK连接
使用openssl server启动一个TLS server
使用openssl client与server建立TLS连接
使用Wireshark解析TLS数据
运行Wireshark,打开tls_psk.cap文件,为了Wireshark能够解析密文,还需要配置Pre-Shared Key,点击编辑 -> 首选项 -> Protocols -> TLS,在Pre-Shared Key中输入密钥值000102030405060708090A0B0C0D0E0F
最终得到TLS数据如下
Client Hello
16 03 01 00 7d 01 00 00 79 03 03 36 9e 43 08 87 60 b8 b1 e0 61 cd d8 1f 4b ab 5e e1 38 08 c2 d9 d5 50 9d 92 61 3e af 62 55 f6 be 00 00 04 00 ae 00 ff 01 00 00 4c 00 00 00 0e 00 0c 00 00 09 6c 6f 63 61 6c 68 6f 73 74 00 23 00 00 00 16 00 00 00 17 00 00 00 0d 00 2a 00 28 04 03 05 03 06 03 08 07 08 08 08 09 08 0a 08 0b 08 04 08 05 08 06 04 01 05 01 06 01 03 03 03 01 03 02 04 02 05 02 06 02
Server Hello
16 03 03 00 59 02 00 00 55 03 03 c5 b4 a2 ca 4e fb 20 21 74 75 fc 9f 91 78 38 ee 47 30 74 8f 8e d9 e4 56 a9 bf 94 0d 55 eb cb 22 20 9e e6 cb c2 77 ad db 29 05 52 eb ce 60 81 fa 71 43 63 5b 9b c9 31 84 f0 88 09 dc d0 21 8a ff 48 00 ae 00 00 0d ff 01 00 01 00 00 16 00 00 00 17 00 00
Server Hello Done
16 03 03 00 04 0e 00 00 00
Client Key Exchange
16 03 03 00 0f 10 00 00 0b 00 09 6d 79 5f 70 73 6b 5f 69 64
Client Change Cipher Spec
14 03 03 00 01 01
Client Finished
16 03 03 00 50 98 9c 70 7f 7b b2 d1 9d 38 70 c1 b0 81 8d 60 97 07 cd 28 0b 61 64 bf ae 46 08 22 90 34 56 82 3a 5e b4 4a e0 51 bd df 9b 34 44 92 67 1b 6c 22 44 60 92 03 e8 d4 f7 09 12 37 56 30 b7 ae 1f 7b 67 58 d5 72 ce 4e 36 89 a6 5a 90 d3 15 2f 53 1a 6c
Server Change Cipher Spec
14 03 03 00 01 01
Server Finished
16 03 03 00 50 41 54 6e f1 7a 2a 3f 1d 0f 26 29 95 7f ba 0a f2 d8 4b 4a 9a c5 b3 b4 71 2e ef 70 fa 39 17 5e cc 90 9f 33 16 c2 e3 ad 4a 3c 95 f1 fe 90 cd cc a1 98 d8 7c 0a b4 d6 50 3a fa 6a 16 f5 6e ff 06 77 a7 f4 66 db d2 fb b4 be cf 8d 7b c8 7c 78 78 71
Client Application Data
17 03 03 00 40 19 77 d0 be 1f b4 83 f9 53 6d 8c f5 06 05 b3 10 e9 98 68 ed bf 30 93 e1 74 cc 22 3a 3b 31 a3 93 26 e2 0c 1c c3 c8 eb d9 f6 ec 11 5a b5 a1 bd 65 7c 28 e4 5b 0d c5 08 65 b7 f3 4a 93 91 83 8a 3a
Server Application Data
17 03 03 00 40 07 80 69 09 82 fd c3 7f 28 18 81 ad 78 4b 78 45 12 fb 47 ca 97 60 a2 50 65 32 97 d4 9e 2c 65 48 2a e3 f3 2b 3b d1 82 6c 30 e1 e8 45 46 32 e0 ca b0 40 96 26 98 fc 4f db d4 c2 33 23 10 a8 6c f3
Alert(Client Close Notify)
15 03 03 00 40 4f aa 08 0c ec c9 19 a0 87 e5 8d f8 69 49 70 54 ac bf 46 58 d5 76 18 1e 22 77 57 1b 86 9d 03 c5 b5 4d 7b 2b a1 e8 29 00 07 23 41 be e6 ad c0 23 61 ec b6 d3 83 dd 0f ed 06 43 c5 03 c9 89 4c ac
消息数据的每个字节含义在wireshark中已提示得很清晰,就不一一解释了,本文演示例子抓包的数据文件可以点击tls_psk.pcap下载,方便跟着文章一步一步计算
这里重点关注加解密这一块,接下来将还原Finished,Application Data及Alert消息的密文构造过程
从Client Hello及Server Hello消息可知
并且协商使用了下面这两个扩展(extension)
这两个扩展要重点关注,他们改变了RFC5246中定义的TLS1.2协议的默认行为
首先计算master secret,要特别注意的是例子中使用了extended master secret extension,要参考rfc7627#section-4而非RFC5246,rfc7627定义如下
session_hash定义在RFC7627#section-3
PSK的premaster secret定义在RFC4279#section-2
PRF(Pseudorandom Function)定义在RFC5246#section-5
针对密码套件PSK-AES128-CBC-SHA256,PRF使用的hash算法为SHA256,在RFC5487#section-3.1中描述
有了规范依据后,接下来代入实际数据进行计算
接下来计算client/server的write key及mac key,需要先生成key block,定义在RFC5246#section-6.3
根据RFC5246#appendix-C
可知PSK-AES128-CBC-SHA256密码套件的enc_key_length为16字节,mac_key_length为32字节
因此key_block至少需要 2 32 + 2 16 = 96字节,P_hash需要迭代3次生成96字节数据
到这里,用于加密及计算MAC的密钥已经得到了
接下来解析finished message,还是先介绍理论依据,根据RFC5246#section-7.4.9,Finished message定义如下,包含了12字节的verify_data
接下来介绍TLS-PSK是如何对消息进行MAC的计算及加密的,需要特别注意的是上述例子client与server协商使用了encrypt-then-mac extension,MAC的计算需要参考RFC7366#section-3
sequence number见RFC5246#section-6.1
个人理解简单来说就是一个8字节的值,初始值为0,client及server针对接收和发送消息都维护自己的两个sequence number,每次接收消息后read sequence number + 1,每次发送消息后write sequence number+1
而加密规则还是需要参考RFC5246#section-6.2.3,要注意GenericBlockCipher中的MAC计算以RFC7366规范为准
接下来代入具体数值还原client finished message
Server finished message的解析过程类似,理论层面不再赘述,直接代入具体数值计算
client application data及server application data的加密及MAC计算与Finished也是一样的,这里只演示client application data的计算
解密Alert(Client Close Notify)