打算简单说下三个方面:证书消息,证书请求消息,证书 verify 消息。
TLS1.3 和 TLS1.2 版本的证书消息本质上差别不大,还是老的消息发出来,格式基本一致。主要不同的是 TLS1.2 中的证书链是一个证书跟着一个证书的,也就是一个 cert entry 跟着另一个 cert entry,每一个 cert entry 验证下一个 cert entry。下面就是 TLS1.2 当中的证书结构,这里 ASN.1Cert 就是一个 cert entry,每个都是标准的 X.509v3 结构
opaque ASN.1Cert<1..2^24-1>;
struct {
ASN.1Cert certificate_list<0..2^24-1>;
} Certificate;
而 TLS1.3 中的 certificate 消息现包含一个 certificate request context,这个字段是应对于 certificate request 中的 certificate request context,提供一次保护性的。对于服务端而言他没收到 certificate request,自然这个字段长度为 0. 另外一点是 TLS1.3 中证书 list 包含的单条证书被拓展隔开了,格式变成了每一个证书后面都有一个拓展字段,隔开下一条证书。
struct {
select (certificate_type) {
case RawPublicKey:
/* From RFC 7250 ASN.1_subjectPublicKeyInfo */
opaque ASN1_subjectPublicKeyInfo<1..2^24-1>;
case X509:
opaque cert_data<1..2^24-1>;
};
Extension extensions<0..2^16-1>;
} CertificateEntry;
目前服务端拓展只支持两种 OCSP Status extension [RFC6066] 和 SignedCertificateTimestamp extension[RFC6962],服务端证书当中的拓展必须和 ClientHello 报文的拓展对应,而客户端证书的拓展也必须和 CertificateRequest 证书当中的保持一致。当然 ustack 系统中目前不需要考虑这些。
最后一点变化是目前整数类型可以为 RawPublicKey,但是一般情况下在 EE 报文中并不会特意协商类型为 RawPublicKey,也就是不会在 server_certificate_type 拓展中协商为 RawPublicKey 所以一般格式还是 X.509v3 的标准证书格式。上面的 cert_data 就是原先的 ASN.1Cert
功能上 certificate request 消息并没有变化,还是请求客户端证书的作用。 但是 TLS1.2 中结构复杂,结构为
struct {
ClientCertificateType certificate_types<1..2^8-1>;
SignatureAndHashAlgorithm
supported_signature_algorithms<2^16-1>;
DistinguishedName certificate_authorities<0..2^16-1>;
} CertificateRequest;
TLS1.3 中,结构稍微简化为:
struct {
opaque certificate_request_context<0..2^8-1>;
Extension extensions<2..2^16-1>;
} CertificateRequest;
certificate_request_context 根据报文种类变化,如果是 PHA 报文,那么生产长度为 32 的随机数据填充。如果不是 PHA 报文,那么长度为 0.extensions 字段则包括了过去的 signature_algorithms 和 DistinguishedName 字段,从而使结构更加清楚。
TLS1.3 Certificate Verify 用来证明发送的证书属于发送方,并保护前面发送的数据的完整性。TLS1.2 只用 Certificate Verify 消息来验证客户端证书的完整性. TLS1.3 中,这条消息必须紧贴 certificate message,且在 finish 之前。
TLS1.3 中这条消息如何计算?首选需要计算计算的主体:主体计算方式为: Transcript-Hash(Handshake Context, Certificate), 主体计算完成之后需要如下信息拼接到一起:
- 重复 64 遍的 0x20 字符串
- 不同的环境标签,对于服务端是"TLS 1.3, server CertificateVerify"。对客户端是"TLS 1.3, client CertificateVerify"
- 一个单 byte 的 0,拓展到报文里实际上是个 0x00
- 上面计算出来的主体
拼接到一起之后,利用和刚才发送的证书相关的签名秘钥进行计算。
TLS1.2 中这的 certificate verify 只给客户用,所以我们实际上需要对应 Server key exchange 消息,但是被签名出来的消息是:
struct {
select (KeyExchangeAlgorithm) {
case dh_anon:
ServerDHParams params;
case dhe_dss:
case dhe_rsa:
ServerDHParams params;
digitally-signed struct {
opaque client_random[32];
opaque server_random[32];
ServerDHParams params;
} signed_params;
case rsa:
case dh_dss:
case dh_rsa:
struct {} ;
/* message is omitted for rsa, dh_dss, and dh_rsa */
/* may be extended, e.g., for ECDH -- see [TLSECC] */
};
} ServerKeyExchange;
拼接完成之后就可以计算签名。
如果实在好奇可以找我要这些网络抓包和秘钥文件,方便学习。我实在是懒得再截图啥的了。
嗯,再次宣传我司 ArrayNetworks 公司,性能好,价格低 LUL
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.