TLCP 概述
TLCP 是利用密码技术,为两个应用程序之间提供保密性和数据的完整性。协议用的密码算法包含非对称密码算法、分组密码算法、密码杂凑算法、数据扩展函数和伪随机函数、协议用到的密钥种类包含服务端密钥、客户端密钥、预主密钥、主密钥和工作密钥。
密钥种类
服务端密钥
服务端密钥为非对称密码算法的密钥对,包括签名密钥对和加密密钥对,其中签名密钥用于握手过程中服务端身份鉴别,加密密钥对用于预主密钥的协商。
客户端密钥
客户端密钥为非对称密码算法的密钥对,包括签名密钥对和加密密钥对,其中签名密钥用于握手过程中服务端身份鉴别,加密密钥对用于预主密钥的协商。
预主密钥
预主密钥(pre_master_secret)是双方协商生成的密钥素材,用于生成主密钥。
主密钥
主密钥(master_secret)由预主密钥、客户端随机数、服务端随机数、常量字符串,经计算生成的 48 字节密钥素材,用于生成工作密钥
工作密钥
工作密钥包括数据加密密钥和校验密钥,其中数据加密密钥用于数据的机密和解密,校验密钥用于数据的完整性计算和校验。发送方使用的工作密钥称为写密钥,接收方使用的工作密钥称为读密钥。
握手协议流程
握手过程如下: 客户端发送客户端 hello 消息给服务端,服务端应向回应服务端 hello 消息,否则产生一个致命错误并且断开连接。客户端 hello 和服务端 hello 用于在客户端和服务端进行基于 SM2、RSA 或 IBC 的密码算法协商,以及确定安全传输能力,包括协议版本、会话标识、密码套件等属性,并且产生和交换随机数。
在客户端 hello 和服务端 hello 消息之后是身份验证和密钥交换过程。包括服务端证书、服务端密钥交换,客户端证书,客户端密钥交换。
在服务端发送完 hello 消息之后,接着发送自己的证书消息,服务端密钥交换信息。如果服务端需要验证客户端的身份,则向客户端发送证书请求消息。然后发送服务端 hello 完成消息,表示 hello 消息阶段已经结束,服务端等待客户端的返回消息。如果服务端发送了一个证书请求消息, 客户端应返回一个证书消息。然后客户端发送密钥交换消息,消息内容取决于客户端 hello 消息和服务端 hello 消息协商出的密钥交换算法。如果客户端发送了证书消息,那么也应发送一个带数字签名的证书验证消息供服务端验证客户端的身份。
接着客户端发送密码规格变更消息,然后客户端立即使用刚协商的算法和密钥,加密并发送握手结束消息。服务端则回应密码规格变更消息,使用刚协商的算法和密钥,加密并发送握手结束消息。至此握手过程结束,服务端和客户端可以开始数据安全传输。
握手消息流程如下:
注: * 表示可选货依赖于上下文关系的消息,不是每次都发送。[ ] 不属于握手协议消息。
如果客户端和服务端决定重用之前的会话,可不必重新协商安全参数。客户端发送客户端 hello 消息,并且带上要重用的会话标识。如果服务端有匹配的会话存在,服务端则使用相应的会话状态接受连接,发送一个具有相同会话标识的服务端 hello 消息。然后客户端和服务端各自发送密码规格变更消息和握手结束消息。至此握手过程结束,服务端和客户端可以开始数据安全传输。如果服务端没鱼匹配的会话标识,服务端会生成一个新的会话标识进行一个完整的握手过程。
会话重用的握手消息如下:
计算函数
数据扩展函数 P_hash
P_hash(secret, seed) = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + HMAC(secret, A(3) + seed) + ···
其中:
- secret 是进行计算所需要的密钥
- seed 是进行计算所需要的数据
- A(0) = seed
- A(i) = HMAC(secret, A(i-1))
- P_hash 能够反复迭代直至产生要求长度的数据
伪随机函数 PRF
PRF(secret, label, seed) = P_hash(secret, label + seed)
密码计算
主密钥计算
主密钥由46个字节组成,由预主密钥、客户端随机数,服务端随机数,常量字符串,经PRF计算生成:
计算方法如下:
m a s t e r s e c r e t = P R F ( p r e m a s t e r s e c r e t , " m a s t e r s e c r e t " , C l i e n t H e l l o . r a n d o m + S e r v e r H e l l o . r a n d o m ) [ 0..47 ] master_secret =PRF(pre_master_secret,"master secret",ClientHello.random +Server Hello.random)[0..47] mastersecret=PRF(premastersecret,"mastersecret",ClientHello.random+ServerHello.random)[0..47]
工作密钥
工作密钥包括校验密钥和加密密钥,具体密钥长度由选用的密码算法决定,由主密钥、客户端随机数,服务器随机数,常量字符串,经PRF计算生成
计算方法如下:
k
e
y
b
l
o
c
k
=
P
R
F
(
S
e
c
u
r
i
t
y
P
a
r
a
m
e
t
e
r
s
.
m
a
s
t
e
r
s
e
c
r
e
t
,
"
k
e
y
e
x
p
a
n
s
i
o
n
"
,
S
e
c
u
r
i
t
y
P
a
r
a
m
e
t
e
r
s
.
s
e
r
v
e
r
r
a
n
d
o
m
+
S
e
c
u
r
i
t
y
P
a
r
a
m
e
t
e
r
s
.
c
l
i
e
n
t
r
a
n
d
o
m
)
;
key_block =PRF(SecurityParameters.master_secret,"key expansion",SecurityParameters.server_random +SecurityParameters.client_random);
keyblock=PRF(SecurityParameters.mastersecret,"keyexpansion",SecurityParameters.serverrandom+SecurityParameters.clientrandom);
知道生成所需长度的输出,然后按顺序分割得到所需的密钥
- 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.fixed_iv_length]
- server_write_IV[SecurityParameters.fixed_iv_length]
根据公式,可编写 Python 代码,内容如下:
*PS:大多数内容还是过于教条,苦于手头没有一个很好的数据实例,如果某位师傅有很好的作为例子的数据可以互相交流学习一下( ̄︶ ̄)。