RFC8446 TLS1.3中文版(4-4)

4. 握手协议

握手协议用于协商连接的安全参数。把握手消息传递给TLS记录层,TLS记录层把它们封装到一个或多个TLSPlaintext或TLSCiphertext中,然后按照当前活动连接状态的规定进行处理和传输。

   enum {
          client_hello(1),
          server_hello(2),
          new_session_ticket(4),
          end_of_early_data(5),
          encrypted_extensions(8),
          certificate(11),
          certificate_request(13),
          certificate_verify(15),
          finished(20),
          key_update(24),
          message_hash(254),
          (255)
      } HandshakeType;

      struct {
          HandshakeType msg_type;    /* handshake type */
          uint24 length;             /* remaining bytes in message */
          select (Handshake.msg_type) {
              case client_hello:          ClientHello;
              case server_hello:          ServerHello;
              case end_of_early_data:     EndOfEarlyData;
              case encrypted_extensions:  EncryptedExtensions;
              case certificate_request:   CertificateRequest;
              case certificate:           Certificate;
              case certificate_verify:    CertificateVerify;
              case finished:              Finished;
              case new_session_ticket:    NewSessionTicket;
              case key_update:            KeyUpdate;
          };
      } Handshake;

协议消息必须以4.4.1中定义的顺序发送,这也已经在第2章的图中展示了。对端如果收到了不按顺序发送的握手消息必须使用一个"unexpected_message"警报来中止握手。
新的握手消息类型已经由IANA指定并在第11章中描述。

4.1. 密钥交换消息

        密钥交换消息用于确定client和server之间的安全能力,并确定包括流量密钥在内的共享密钥来保护其余的握手消息和数据。

4.1.1. 密码协商

    在TLS中,密码协商通过client在ClientHello中提供下面4个选项集合来实现:

  •  一个密码套件列表,指的是client所支持的AEAD算法或HKDF hash对。
  • 一个supported_groups(4.2.7)扩展,指的是client支持的(EC)DHE组;一个key_share(4.2.8)扩展,包含了一些或全部组所的(EC)DHE共享秘钥(shares )。
  • 一个signature_algorithms(4.2.3)扩展,指的是client能接受的签名算法;可能会添加一个signature_algorithms_cert扩展(4.2.3)来指明证书指定的签名算法。
  • 一个pre_shared_key (4.2.11)扩展,包含了一个client知晓的对称密钥;一个psk_key_exchange_modes (4.2.9) 扩展,表明与PSK一起使用的密钥交换模式。

       如果server没有选择PSK,则这些选项的前3个是完全正交的:server独立地选择一个密码套件,一个(EC)DHE组和用于确定密钥的密钥共享,一个签名算法或证书对用于认证自己和client。如果接收到的supported_groups和server所支持的组之间没有重叠,则server必须用一个handshake_failure 或一个insufficient_security警报中止握手。
       如果server选择了一个PSK,则必须从client的"psk_key_exchange_modes"扩展(目前是仅PSK或带(EC)DHE)所携带的集合中选择一个密钥建立模式。需要注意的是如果可以不带(EC)DHE就使用PSK,则"supported_groups"参数不重叠不一定是致命的,就像之前讨论过的非PSK场景一样。
      如果server选择了一个(EC)DHE组并且client没有在初始ClientHello中提供兼容的"key_share"扩展,server必须响应HelloRetryRequest(4.1.4)消息。

      如果server成功地选择了参数且没有发送HelloRetryRequest,表明ServerHello中所选的参数如下:

  • 如果使用PSK,则server会发送一个pre_shared_key扩展表明所选的密钥。
  • 使用 (EC)DHE时,server也会提供key_share扩展。如果没有使用PSK,则一直使用(EC)DHE和基于证书的认证。
  • 当通过证书进行验证时,server将会发送Certificate(4.4.2)和CertificateVerify(4.4.3)消息。在本文定义的TLS1.3中,会一直使用一个PSK或一个证书,但两者不会同时使用。将来的文档可能会定义怎样同时使用它们。

       如果server不能协商出支持的参数集合(例如,client和server的参数集合没有重叠),它必须用一个handshake_failure或一个insufficient_security警报(见第6章)中止握手。

4.1.2. Client Hello

       当client第一次连接server时,需要发送ClientHello作为第一个消息。当server用HelloRetryRequest来响应ClientHello时,client也应当发送ClientHello。这种条件下,client必须发送相同的ClientHello(无修改),除非:

  • 如果HelloRetryRequest带有一个key_share扩展,则将共享列表用包含指定组中的一个KeyShareEntry的列表取代。
  • 如果存在early_data扩展则将其移除。Early data不允许在HelloRetryRequest之后出现。
  • 如果HelloRetryRequest中提供了一个cookie扩展,则需要也包含一个cookie扩展。
  • 如果需要重新计算obfuscated_ticket_age和binder则更新pre_shared_key扩展、(可选地)删除与server指定的密码族不兼容的任何PSK。
  • 选择性增加、删除或更改padding扩展[RFC 7685]的长度。
  • 将来定义的其他HelloRetryRequest中扩展允许的修改。

       由于TLS 1.3禁止重协商,如果server已经协商完成了TLS 1.3,在任何其它时间收到了ClientHello,必须用unexpected_message警报中止连接。
       如果server用以前版本的TLS建立了连接并在重协商时接收了一个TLS1.3的ClientHello,它必须保持以前的协议版本,不能协商TLS 1.3。
      这个消息的结构:

      uint16 ProtocolVersion;
      opaque Random[32];
      uint8 CipherSuite[2];    /* Cryptographic suite selector */

      struct {
          ProtocolVersion legacy_version = 0x0303;    /* TLS v1.2 */
          Random random;
          opaque legacy_session_id<0..32>;
          CipherSuite cipher_suites<2..2^16-2>;
          opaque legacy_compression_methods<1..2^8-1>;
          Extension extensions<8..2^16-1>;
      } ClientHello;

legacy_version:在以前版本的TLS里,这个字段用于版本协商和表示client支持的最高版本号。经验表明很多server没有适当地实现版本协商,导致“版本容忍”,会使server拒绝本来可接受的版本号高于支持的ClientHello。在TLS 1.3中,client在"supported_versions" 扩展(4.2.1节)中表明版本偏好,且legacy_version字段必须设置为TLS1.2的版本号0x0303。TLS 1.3 ClientHello的legacy_version为0x0303,supported_versions扩展值为最好版本0x0304(关于后向兼容的细节见附录D)

random: 由一个安全随机数生成器产生的32字节随机数,更多信息见附录C。

legacy_session_id: 之前的TLS版本支持"会话恢复"特性,在TLS 1.3中此特性与预共享密钥合并了(见2.2)。client需要将此字段设置为TLS 1.3之前版本的server缓存的 session ID。在兼容模式下(见附录D.4)此字段必须非空,所以一个不能提供TLS 1.3之前版本会话的client必须产生一个32字节的新值。这个值不必是随机的但应该是不可预测的以避免实现上固定为一个具体的值(也被称为僵化)。否则,它必须被设置为一个0长度向量(例如,一个0字节长度字段)。

cipher_suites: client支持的对称密码族选项列表,具体有记录保护算法(包括密钥长度)和与HKDF一起使用的hash算法,这些算法以client偏好降序排列。值定义在附录B.4。如果列表中包含server不认识,不支持,或不想使用的密码族,server必须忽略这些密码族并且正常处理其余的密码族。如果client试图确定一个PSK,应该通告至少一个密码族以表明PSK关联hash算法。

legacy_compression_methods:TLS 1.3以前的版本支持压缩,并提供一个支持的压缩方法列表。对于TLS 1.3的每个ClientHello,这个向量必须只包含1字节并设置为0,对应以前TLS版本的"null"压缩方法。如果收到的TLS 1.3 ClientHello这个字段是其它的值,server必须以一个"illegal_parameter" alert来中止握手。请注意,TLS 1.3 server可能会接收TLS 1.2或之前的ClientHello包含其它压缩方法,(如果与这样一个先前版本协商)必须遵循适当的TLS先前版本的流程。

extension: Client通过在extension字段发送数据来向server请求扩展的功能。实际的"Extension"格式定义在4.2中。TLS 1.3强制使用特定的extension,因为一些功能被转移到了extension里以保留ClientHello与先前TLS版本的兼容性。Server必须忽略无法识别的extension。

所有版本的TLS都允许compression_method字段后跟着extension字段。TLS 1.3 ClientHello消息始终包含extension(至少包含"supported_versions",否则将被当做TLS 1.2 ClientHello消息)。但是,TLS1.3 server可能会从以前版本的TLS接收不带extension字段的ClientHello消息。可以通过ClientHello末尾compression_methods字段后面是否有字节来检测扩展的存在。注意,这种检测可选数据的方法不同于检测可变长度字段的常规TLS方法,但是可用于与extension定义之前的TLS版本兼容。TLS 1.3 server需要首先执行此检查,并且只有存在supported_versions扩展时才尝试协商TLS 1.3。如果协商TLS 1.3之前的版本,server必须检查消息是否在legacy_compression_methods之后不包含任何数据,或者是否包含一个后面没有数据的有效扩展块。如果不是,则必须以“decode_error”alert中止握手。

如果client使用extension请求附加功能,而server不提供此功能,则client可能会中止握手。
client发送ClientHello消息后,等待ServerHello或HelloRetryRequest消息。如果正在使用早期数据,则client可以在等待下一个握手消息时传输早期应用数据(2.3节)。

4.1.3. Server Hello

当可以根据ClientHello协商出一组可接受的握手参数时,server将发送此消息响应ClientHello消息以继续握手流程。

此消息的结构:

 struct {
      ProtocolVersion legacy_version = 0x0303;    /* TLS v1.2 */
      Random random;
      opaque legacy_session_id_echo<0..32>;
      CipherSuite cipher_suite;
      uint8 legacy_compression_method = 0;
      Extension extensions<6..2^16-1>;
  } ServerHello;

legacy_version:在此前的TLS版本中,此字段用来版本协商和承载连接的选定版本号。不幸的是,当存在新值时,一些中间件会处理失败。在TLS1.3中,TLS sever使用"supported_versions"扩展来表明它的版本(4.2.1节),legacy_version字段必须设置为TLS1.2的版本号0x0303。(前向兼容具体见附录D)

random:由安全随机数生成器生成的32字节,其他信息见附录C。如果协商TLS 1.2或TLS 1.1,最后8个字节必须被重写,如下所述。该结构由server生成,必须与ClientHello.random分别生成。

legacy_session_id_echo:client的legacy_session_id字段内容。请注意,即使客户端的值与服务器选择不恢复的缓存的TLS 1.3之前会话相对应,也会回复此字段。如果客户端接收到与发送内容不匹配的legacy_session_id_echo字段,则必须使用“illegal_parameter”alert中止握手。

cipher_suite:服务器从ClientHello.cipher_suites列表中选择的单个密码套件。 客户端接收到自己没有提供的密码套件必须中止握手。

legacy_compression_method: 一个字节,值必须是0。

extensions:扩展列表。 ServerHello必须只包含确定加密上下文和协商版本号所需的扩展。所有当前的TLS 1.3 ServerHello消息必须包含supported_versions扩展。目前ServerHello消息额外包含"pre_shared_key"扩展和"key_share"扩展之一,或者两者都包含(当使用PSK和(EC)DHE秘钥建立)。其他扩展(见4.2节)单独在EncryptedExtensions消息里发送。
由于中间件的后向兼容性原因(见附录D.4),HelloRetryRequest消息与ServerHello使用相同的结构体,但带有设置为特殊值("HelloRetryRequest"的SHA-256)的随机值:
CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91
C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C

在接收到类型为server_hello的消息时,必须首先检查随机值,如果该值与此匹配,则按照4.1.4所述进行处理。
TLS 1.3使用服务器的随机值实现降级保护机制。 TLS 1.3服务器以TLS 1.2或更低版本响应ClientHello必须专门设置随机值的最后8个字节。
如果协商TLS 1.2,TLS 1.3服务器必须将随机值的最后8个字节设置为:
44 4F 57 4E 47 52 44 01

如果协商 TLS 1.1或更低版本,TLS 1.3服务器和TLS 1.2服务器应该将随机值的 最后8个字节设置为:

 44 4F 57 4E 47 52 44 00

TLS 1.3客户端接收到TLS 1.2或更低版本的ServerHello必须检查最后八个字节不等于这些值。如果ServerHello指示TLS 1.1或更低版本,TLS 1.2客户端还应该检查最低八字节不等于第二个值。 客户端如果找到匹配值则必须用“illegal_parameter”alert中止握手。这种机制提供了有限的保护,以防止降级攻击超出Finished交换提供内容:因为TLS1.2及以下版本的ServerKeyExchange消息包含两个随机值的签名,只要使用临时密码,攻击者就不可能探测修改随机值。当使用静态RSA时就不提供降级保护。

注意:这是从[RFC5246]开始修改的,所以实际上许多TLS 1.2客户端和服务器可能不会像上面说的那样。

旧的TLS客户端使用TLS 1.2或之前的版本执行重协商,重协商期间如果接收到TLS 1.3的ServerHello,必须使用"protocol_version" alert终止握手。需要注意的是,如果TLS 1.3已经协商好了就不会重协商了。

4.1.4. HelloRetryRequest

       如果可以找到可接受的一组参数,但ClientHello中没有足够的信息继续处理握手流程,服务器就将这个消息作为ClientHello的回应。如4.1.3所说,HelloRetryRequest跟ServerHello格式一样,并且legacy_version、legacy_session_id_echo、cipher_suite和legacy_compression_method字段的含义也一样。然而,为方便起见,本文中把HelloRetryRequest当做不同的消息讨论。
       服务器扩展必须包含supported_versions。另外,应该包含客户端产生正确ClientHello的必须扩展最小集。像ServerHello一样,HelloRetryRequest不能包含客户端ClientHello中未提供的扩展,可选cookie扩展除外(4.2.2节)。
     Client接收到HelloRetryRequest必须如4.1.3所说的检查egacy_version、legacy_session_id_echo、cipher_suite和legacy_compression_method,然后处理扩展,先用supported_versions确定版本号。如果HelloRetryRequest不会对ClientHello造成任何改变, Client必须以illegal_parameter alert终止握手。如果client在同一个链接中收到第二个HelloRetryRequest(如即以ClientHello本身响应HelloRetryRequest),必须以unexpected_message alert终止连接。
     否则, 客户端必须处理HelloRetryRequest中的所有扩展,并发送第二个更新的ClientHello。本规范中定义的HelloRetryRequest扩展包括:

  • supported_versions (见4.2.1)
  • cookie (见4.2.2)
  • key_share (见4.2.8)

       Client接收到一个没有提供过的密码套件必须终止握手。Server接收到合法更新的ClientHello时,必须确保协商相同密码套件(如果server选择了协商过程中第一步的密码套件,这就会自动发生)。client接收到ServerHello后,必须检查其中的密码套件是否与HelloRetryRequest中的相同,否则以"illegal_parameter" alert终止握手。
      此外,在更新的ClientHello中,客户端不应提供与所选密码套件以外的哈希相关联的任何PSK。 这允许客户端避免在第二个ClientHello中计算多个哈希的部分哈希转录。
      接收未提供的密码套件的客户端必须中止握手。 服务器必须确保在接收到一致的更新ClientHello时协商相同的密码套件(如果服务器选择密码套件作为协商的第一步,则会自动发生)。 客户端收到ServerHello后必须检查ServerHello中提供的密码套件是否与HelloRetryRequest中的密码套件相同,否则将以“illegal_parameter”警报中止握手。
       HelloRetryRequest 中的supported_versions扩展的selected_version值必须在ServerHello中保留,如果值变化了,client必须以"illegal_parameter" alert终止握手

4.2. 扩展

许多TLS消息包含tag-length-value编码的扩展结构:

  struct {
        ExtensionType extension_type;
        opaque extension_data<0..2^16-1>;
    } Extension;

    enum {
        server_name(0),                             /* RFC 6066 */
        max_fragment_length(1),                     /* RFC 6066 */
        status_request(5),                          /* RFC 6066 */
        supported_groups(10),                       /* RFC 8422, 7919 */
        signature_algorithms(13),                   /* RFC 8446 */
        use_srtp(14),                               /* RFC 5764 */
        heartbeat(15),                              /* RFC 6520 */
        application_layer_protocol_negotiation(16), /* RFC 7301 */
        signed_certificate_timestamp(18),           /* RFC 6962 */
        client_certificate_type(19),                /* RFC 7250 */
        server_certificate_type(20),                /* RFC 7250 */
        padding(21),                                /* RFC 7685 */
        pre_shared_key(41),                         /* RFC 8446 */
        early_data(42),                             /* RFC 8446 */
        supported_versions(43),                     /* RFC 8446 */
        cookie(44),                                 /* RFC 8446 */
        psk_key_exchange_modes(45),                 /* RFC 8446 */
        certificate_authorities(47),                /* RFC 8446 */
        oid_filters(48),                            /* RFC 8446 */
        post_handshake_auth(49),                    /* RFC 8446 */
        signature_algorithms_cert(50),              /* RFC 8446 */
        key_share(51),                              /* RFC 8446 */
        (65535)
    } ExtensionType;

这里:

  • "extension_type"表示指定扩展类型。 
  • "extension_data"包含指定扩展类型的信息。

扩展类型表由IANA维护,见11章。

扩展通常以请求/响应方式构造,尽管一些扩展仅仅是没有相应响应的指示。客户端在ClientHello消息中发送其扩展请求,服务器在ServerHello、EncryptedExtensions和HelloRetryRequest消息中发送扩展响应。服务器在CertificateRequest消息中发送扩展请求,客户端可能以Certificate消息进行响应。服务器也可以在NewSessionTicket中发送未经请求的扩展,但客户端不直接响应这些。
如果对端没有发送相应的扩展请求(除HelloRetryRequest中的cookie扩展外),严禁发送扩展响应。在接收到这样的扩展时,端点必须用“unsupported_extension”警报中止握手。
下表列出了给定扩展可能出现的消息:CH(ClientHello),SH(ServerHello),EE(EncryptedExtensions),CT(Certificate),CR(CertificateRequest),NST(NewSessionTicket)和HRR ( HelloRetryRequest)。如果接收到可识别的扩展,并且对应消息未指定它,必须用“illegal_parameter”警报来中止握手。

   +--------------------------------------------------+-------------+
   | Extension                                        |     TLS 1.3 |
   +--------------------------------------------------+-------------+
   | server_name [RFC6066]                            |      CH, EE |
   |                                                  |             |
   | max_fragment_length [RFC6066]                    |      CH, EE |
   |                                                  |             |
   | status_request [RFC6066]                         |  CH, CR, CT |
   |                                                  |             |
   | supported_groups [RFC7919]                       |      CH, EE |
   |                                                  |             |
   | signature_algorithms (RFC 8446)                  |      CH, CR |
   |                                                  |             |
   | use_srtp [RFC5764]                               |      CH, EE |
   |                                                  |             |
   | heartbeat [RFC6520]                              |      CH, EE |
   |                                                  |             |
   | application_layer_protocol_negotiation [RFC7301] |      CH, EE |
   |                                                  |             |
   | signed_certificate_timestamp [RFC6962]           |  CH, CR, CT |
   |                                                  |             |
   | client_certificate_type [RFC7250]                |      CH, EE |
   |                                                  |             |
   | server_certificate_type [RFC7250]                |      CH, EE |
   |                                                  |             |
   | padding [RFC7685]                                |          CH |
   |                                                  |             |
   | key_share (RFC 8446)                             | CH, SH, HRR |
   |                                                  |             |
   | pre_shared_key (RFC 8446)                        |      CH, SH |
   |                                                  |             |
   | psk_key_exchange_modes (RFC 8446)                |          CH |
   |                                                  |             |
   | early_data (RFC 8446)                            | CH, EE, NST |
   |                                                  |             |
   | cookie (RFC 8446)                                |     CH, HRR |
   |                                                  |             |
   | supported_versions (RFC 8446)                    | CH, SH, HRR |
   |                                                  |             |
   | certificate_authorities (RFC 8446)               |      CH, CR |
   |                                                  |             |
   | oid_filters (RFC 8446)                           |          CR |
   |                                                  |             |
   | post_handshake_auth (RFC 8446)                   |          CH |
   |                                                  |             |
   | signature_algorithms_cert (RFC 8446)             |      CH, CR |
   +--------------------------------------------------+-------------+

       当存在不同类型的多个扩展时,扩展可以以任何顺序出现,但pre_shared_key(4.2.11)必须是ClientHello中的最后一个扩展(但在ServerHello扩展块中可以出现在任何位置)。给定扩展块中同一类型的扩展只能出现一次。
       与TLS 1.2不同的是,TLS 1.3即使在resumption-PSK模式下,每次握手都重新协商扩展。然而,0-RTT参数是在先前握手中协商的,不匹配可能需要拒绝0-RTT(见4.2.7)。
       在新协议中可能会出现的新特性与现有功能之间存在微妙(但也不是那么微妙)交互,这可能会导致整体安全性的显著降低。设计新扩展时,应考虑以下注意事项:

  • 服务器不同意扩展的一些情况是错误条件(如握手无法继续),有些则简单地拒绝支持特定功能。一般来说,前者应该使用错误警报,后者的服务器扩展响应中会有一个字段。
  • 扩展应尽可能设计为防止任何通过操纵握手信息强制使用(或不使用)特定功能的攻击。无论该功能是否会引起安全问题,都应遵循这一原则。通常,扩展字段被包含在Finished消息散列的输入中是足够的,但是当扩展改变在握手阶段中发送消息的含义时,需要特别小心。设计者和实现者应该意识到,在握手被验证之前,主动攻击者可以修改消息并插入,删除或替换扩展。

4.2.1. 支持的版本号

  struct {
      select (Handshake.msg_type) {
          case client_hello:
               ProtocolVersion versions<2..254>;
          case server_hello: /* and HelloRetryRequest */
               ProtocolVersion selected_version;
      };
  } SupportedVersions;

        客户端使用supported_versions扩展来表明自己支持哪些版本的TLS。该扩展包含的版本列表以偏好顺序排列, 第一个是最优先的版本。这个规范的实现必须在扩展中发送包含准备协商的TLS所有版本(对于这个规范,这意味着最低 0x0304,但是如果支持TLS以前的版本,也必须包含进去)。
       如果ClientHello中没有此扩展,那么符合本规范的服务器必须按照[RFC5246]中的规定协商TLS 1.2或先前版本,就算ClientHello.legacy_version为0x0304或更高版本也是如此。Server接收到legacy_version为0x0304的 ClientHello可以终止握手。
      如果ClientHello中有此扩展,服务器禁止使用ClientHello.legacy_version值来进行版本协商,并且必须只使用supported_versions扩展来确定客户端偏好。服务器必须只选择该扩展中存在的TLS版本,并且必须忽略任何未知版本。注意,如果一方支持稀疏范围,这种机制可以协商出TLS 1.2之前的版本。选择支持TLS以前版本的TLS 1.3的实现应支持TLS 1.2。服务器应必须准备好接收包含此扩展的ClientHello,但不要在版本列表中包含0x0304。
        协商TLS 1.3之前版本TLS的服务端必须设置ServerHello.version,且不能发送supported_versions扩展。 协商TLS 1.3的服务器必须以包含选定版本值(0x0304)的supported_versions扩展回应。必须将ServerHello.legacy_version字段设置为0x0303 (TLS 1.2)。客户端必须在处理其余ServerHello之前检查此扩展(虽然必须解析ServerHello来读取扩展)。如果此扩展出现,客户端必须忽略ServerHello.legacy_version值,且必须只使用supported_versions扩展来确定选定版本。如果ServerHello 中的supported_versions扩展包含客户端没提供的版本或者包含TLS 1.3之前的版本,客户端必须以illegal_parameter alert终止握手。

4.2.2. Cookie

  struct {
      opaque cookie<1..2^16-1>;
  } Cookie;

Cookie有两个主要目的:

  • 允许服务器强制客户端展示其网络地址可达性(从而提供DoS保护)。 这主要用于非面向连接的传输(见[RFC6347])。
  • 允许服务器向客户端卸载状态,从而可以发送HelloRetryRequest而不存储任何状态。 服务器通过将ClientHello的哈希存储在HelloRetryRequest cookie中(用一些合适的完整性算法保护)来实现。

       当发送HelloRetryRequest时,服务器可以向客户端提供cookie扩展(这是通常规则的一个例外,即只能发送出现在ClientHello中的扩展)。 当发送新的ClientHello时,客户端必须将HelloRetryRequest中收到的cookie复制到新ClientHello中的cookie扩展中。 客户端不得在后续连接initial ClientHello中使用Cookie。
        无状态服务器可能在第一个和第二个ClientHello之间接收到一个change_cipher_spec类型的未加密数据(见第5章)。因为服务器不存储任何状态,看起来就像接收到的第一个消息。无状态服务器必须忽略这些数据。

4.2.3. 签名算法

        TLS 1.3提供了两个扩展来表示数字签名里会用哪种签名算法。signature_algorithms_cert扩展提供证书中的签名,signature_algorithms扩展是从TLS 1.2出现的,提供CertificateVerify消息中的签名。证书中的秘钥也必须是所使用的签名算法的适当类型。这是RSA密钥和PSS签名的一个特殊问题,如下所述。如果没有signature_algorithms_cert扩展,signature_algorithms扩展也提供证书中的出现的签名。客户端如果期望服务器通过证书证明其身份,必须发送signature_algorithms扩展。如果服务器通过证书证明了其身份,且客户端没有发送signature_algorithms扩展,那么服务器必须以missing_extension alert终止握手(见9.2).
         signature_algorithms_cert扩展允许支持证书的不同算法集的实现明确表达自己的能力。TLS 1.2实现应该也处理这些扩展。两种情况里具有相同策略的实现都可以使用signature_algorithms_cert扩展。
ClientHello扩展中的extension_data字段包含SignatureSchemeList值:

 enum {
      /* RSASSA-PKCS1-v1_5 algorithms */
      rsa_pkcs1_sha256(0x0401),
      rsa_pkcs1_sha384(0x0501),
      rsa_pkcs1_sha512(0x0601),

      /* ECDSA algorithms */
      ecdsa_secp256r1_sha256(0x0403),
      ecdsa_secp384r1_sha384(0x0503),
      ecdsa_secp521r1_sha512(0x0603),

      /* RSASSA-PSS algorithms with public key OID rsaEncryption */
      rsa_pss_rsae_sha256(0x0804),
      rsa_pss_rsae_sha384(0x0805),
      rsa_pss_rsae_sha512(0x0806),

      /* EdDSA algorithms */
      ed25519(0x0807),
      ed448(0x0808),

      /* RSASSA-PSS algorithms with public key OID RSASSA-PSS */
      rsa_pss_pss_sha256(0x0809),
      rsa_pss_pss_sha384(0x080a),
      rsa_pss_pss_sha512(0x080b),

      /* Legacy algorithms */
      rsa_pkcs1_sha1(0x0201),
      ecdsa_sha1(0x0203),

      /* Reserved Code Points */
      private_use(0xFE00..0xFFFF),
      (0xFFFF)
  } SignatureScheme;

  struct {
      SignatureScheme supported_signature_algorithms<2..2^16-2>;
  } SignatureSchemeList;

注意: 此枚举名为SignatureScheme,因为TLS 1.2中已经有一个SignatureAlgorithm类型,将被此替换。 我们在全文中使用术语Signature Algorithms。

        每个SignatureScheme值列出客户端愿意验证的单一签名算法。 这些值以优先级的降序排列。 注意, 签名算法输入任意长度的消息, 而不是摘要。 传统上作用于摘要的算法应在TLS中定义,首先使用指定的哈希算法对输入进行哈希,然后照常处理。 上面列出的码点组具有以下含义:
RSASSA-PKCS1-v1_5 算法:表示使用RSASSA-PKCS1-v1_5 [RFC3447]签名算法和[SHS]中定义的相应哈希算法。这些值仅涉及出现在证书中的签名(见4.4.1.2),并且未定义用于签名TLS握手消息。但它们可能出现在signature_algorithms 和signature_algorithms_cert以对TLS 1.2做前向兼容。
ECDSA 算法:表示使用ECDSA [ECDSA]签名算法,ANSI X9.62 [X962]和FIPS 186-4 [DSS]中定义了相应曲线,[SHS]中定义了相应哈希算法。签名表示为DER-encoded[X690] 的ECDSA-Sig-Value结构。
RSASSA-PSS RSAE 算法:表示使用具有掩码生成功能1的RSASSA-PSS [RFC8017]签名算法。掩码生成函数中使用的摘要和签名的摘要都是在[SHS]中定义的相应哈希算法。盐的长度必须等于摘要输出的长度。如果在X.509证书中携带公钥,必须使用rsaEncryption OID [RFC5280]。
EdDSA 算法:表示使用[RFC8032] 中定义的EdDSA签名算法或其后续算法。注意,这些对应于“PureEdDSA”算法,而不是“prehash”变体。
RSASSA-PSS PSS 算法:表示使用掩码生成函数1的RSASSA-PSS [RFC8017]签名算法 。掩码生成函数中使用的摘要和签名的摘要都是[SHS]中定义的相应哈希算法。如果X.509证书中携带了公钥,必须使用RSASSA-PSS OID [RFC5756]。当在证书签名中使用时,算法参数必须是DER编码的。如果对应的公钥参数存在,算法中的参数必须与公钥参数相同。
遗留算法:表示由于具有已知缺点被弃用的算法,特别是在本文中使用的用RASSA-PKCS1-v1_5的RSA或ECDSA。 这些值仅指出现在证书中的签名(见4.4.2.2),并且未被定义用于签名TLS握手消息,虽然它们可以出现在signature_algorithms和signature_algorithms_cert中以前向兼容TLS 1.2。端点不应该协商这些算法,但允许这样做仅仅是为了前向兼容。 提供这些值的客户端必须将其列为最低优先级(在SignatureSchemeList中的所有其他算法之后列出)。 TLS 1.3服务器不得提供SHA-1签名证书,除非没有此证书就不能生成有效的证书链(见4.4.2.2)。

         自签名证书或作为信任锚的证书上的签名不会生效,因为它们开始了认证路径(见[RFC5280] 3.2)。 开始认证路径的证书可以使用未在signature_algorithms扩展中通告的签名算法。
         注意,TLS 1.2定义了不同扩展。愿意协商TLS 1.2的TLS 1.3实现在协商该版本时必须按照[RFC5246]的要求进行操作。 尤其是:

  • TLS 1.2 ClientHello可以删除此扩展。
  • 在TLS 1.2中,扩展包含hash/signature对。 这些对被编码为两个八位数,因此已分配SignatureScheme值已与TLS 1.2的编码对齐。 一些遗留对保留未分配。 这些算法自TLS 1.3起已弃用,不得提供或协商。 特别是,不得使用MD5 [SLOTH]、SHA-224和DSA。
  • ECDSA签名方案与TLS 1.2的ECDSA哈希/签名对一致。 然而,旧语义没有约束签名曲线。 如果协商TLS 1.2,则必须准备接受使用“supported_groups”扩展中通告的任何曲线的签名。
  • 支持RSASSA-PSS(在TLS 1.3中是强制的)的实现必须准备接受使用该方案的签名,即使协商的是TLS 1.2。 在TLS 1.2中,RSASSA-PSS与RSA密码套件一起使用。

4.2.4. 证书颁发机构(Certificate Authorities)

      certificate_authorities扩展用于指示端点支持的证书授权机构(CA),并且接收端点应该使用它来指导证书选择。
       certificate_authorities扩展的内容由CertificateAuthoritiesExtension结构组成。

  opaque DistinguishedName<1..2^16-1>;
  struct {
      DistinguishedName authorities<3..2^16-1>;
  } CertificateAuthoritiesExtension;

authorities: 可接受证书颁发机构的名称[X501]列表,以 DER编码[X690]格式表示。 这些名称为信任锚或从属CA指定所需名称,因此,该消息可以用于描述已知的信任锚以及期望的授权空间。

         客户端可以在ClientHello消息中发送certificate_authorities扩展。 服务器可以在CertificateRequest消息中发送。
         trusted_ca_keys扩展[RFC6066]目的类似,但更为复杂,TLS 1.3中不使用,但可能出现在先前TLS版本客户端的ClientHello消息中。

4.2.5. OID Filters

        oid_filters扩展允许服务器提供一组希望客户端的证书匹配的OID/value对。如果是服务器提供的扩展,必须只能在CertificateRequest消息中发送。

      struct {
          opaque certificate_extension_oid<1..2^8-1>;
          opaque certificate_extension_values<0..2^16-1>;
      } OIDFilter;

      struct {
          OIDFilter filters<0..2^16-1>;
      } OIDFilterExtension;

filters: 证书扩展OID列表[RFC5280]和允许值,以DER-encoded [X690]格式呈现。一些证书扩展OID允许多个值(如Extended Key Usage)。如果服务器包含了一个非空filters列表,回应的客户端证书必须包含所有客户端认识的指定扩展OID。对于每个客户端认识的扩展OID,所有指定值都必须出现在客户端证书中(但证书也可以有其他值)。然而,客户端必需忽略并跳过任何不认识的扩展OID。如果客户端忽略一些要求的证书扩展OID,并提供一个不满足请求的证书,服务器可以自行决定是在没有客户端认证的情况下继续连接,还是以"unsupported_certificate" alert放弃握手。任何给定的OID禁止在filters列表中出现多次。
         PKIX RFCs定义了各种各样的证书扩展OID和对应值。匹配证书扩展值不一定位相等,取决于类型。TLS实现最好由它们的PKI库使用证书扩展OID执行证书选择。

本文为[RFC5280]中的两个标准证书扩展定义了匹配规则:

  • 当请求中assert的所有key usage位也在Key Usage证书扩展中assert,则证书中的Key Usage扩展与请求匹配。
  • 当请求中所有秘钥用途OID也在Extended Key Usage证书扩展中找到,则证书中的Extended Key Usage扩展与请求匹配。

不同规范可以为其他证书扩展定义匹配规则。

4.2.6. Post-Handshake Client Authentication

           post_handshake_auth扩展用于表示客户端愿意执行握手后的认证(4.6.2)。 服务器禁止向不提供此扩展的客户端发送post-handshake CertificateRequest。 服务器不得发送此扩展。

  struct {} PostHandshakeAuth;

post_handshake_auth扩展的extension_data字段为零长度。

4.2.7. 支持组(supported_groups)

        当客户端发送supported_groups扩展时,表示客户端支持的用于密钥交换的命名组(named groups),顺序从最优选到最不优选。
         注意: 在TLS 1.3之前的TLS版本中,此扩展名称为elliptic_curves,并且只包含椭圆曲线组(见[RFC4492]和[RFC7919])。 此扩展也用于协商ECDSA曲线。签名算法现在独立协商(见4.2.3)。
         此扩展的extension_data字段包含NamedGroupList值:

enum {
      /* Elliptic Curve Groups (ECDHE) */
      secp256r1(0x0017), secp384r1(0x0018), secp521r1(0x0019), x25519(0x001D), x448(0x001E),

      /* Finite Field Groups (DHE) */
      ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102), ffdhe6144(0x0103), ffdhe8192(0x0104),

      /* Reserved Code Points */
      ffdhe_private_use(0x01FC..0x01FF),
      ecdhe_private_use(0xFE00..0xFEFF),
      (0xFFFF)
  } NamedGroup;

  struct {
      NamedGroup named_group_list<2..2^16-1>;
  } NamedGroupList;

Elliptic Curve Groups(ECDHE): 表示支持对应的命名曲线,在FIPS 186-4 [DSS]或[RFC7748]中定义。值0xFE00到0xFEFF保留供私用。
Finite Field Groups(DHE):表示支持相应的有限域组,在[RFC7919]中定义。值0x01FC至0x01FF保留供私用。
        named_group_list中的条目根据发送者的偏好排序(最前面是最偏好的选择)。
        从TLS 1.3开始,服务器可以向客户端发送supported_groups扩展。客户端不能在成功完成握手之前对supported_groups中的任何信息采取行动,但可以使用从成功完成的握手中获得的信息来更改后续连接中key_share扩展中使用的组。如果服务器有一个更偏向于用key_share扩展中信息的组,但仍然愿意接受ClientHello,它应该发送supported_groups来更新客户端的偏好视图。此扩展应包含服务器支持的所有组,无论当前客户端是否支持。

4.2.8. Key Share

key_share扩展包含端点的加密参数。
客户端可以发送空的 client_shares向量,以一个额外的往返代价从服务器请求组选择(见4.1.4)。

  struct {
      NamedGroup group;
      opaque key_exchange<1..2^16-1>;
  } KeyShareEntry;

group:要交换的密钥的命名组。
key_exchange: 密钥交换信息。 此字段的内容由指定组及其相应的定义确定。有限域Diffie-Hellman [DH76]参数在4.2.8.1中描述。椭圆曲线Diffie-Hellman参数在4.2.8.2中描述。

在ClientHello消息中,这个扩展的extension_data字段包含一个KeyShareClientHello值:

  struct {
      KeyShareEntry client_shares<0..2^16-1>;
  } KeyShareClientHello;

client_shares:一组提供的KeyShareEntry值,以客户端偏好降序排列。

       如果客户端请求HelloRetryRequest,则此向量可以为空。 每个KeyShareEntry值必须对应于在supported_groups扩展中提供的组,并且必须以相同的顺序排列。 然而,值可以是supported_groups扩展的非连续子集,并且可以省略最优选的组。如果最优选的组是新的,并且没有足够的空间更高效地预生成共享秘钥,则可能出现这种情况。
      客户端可以提供很多KeyShareEntry值,数量跟提供的支持组一样,每个值表示一组密钥交换参数。例如,客户端可能为几个椭圆曲线或多个FFDHE组提供共享秘钥。每个KeyShareEntry的key_exchange值必须独立生成。 客户不得为同一组提供多个KeyShareEntry值。 客户端不得为supported_groups扩展中未列出的组提供KeyShareEntry值。 服务器可能会检查违反了这些规则的行为,如果违反了则使用illegal_parameter警报来中止握手。
       在HelloRetryRequest消息中,此扩展的extension_data字段包含一个KeyShareHelloRetryRequest值:

  struct {
      NamedGroup selected_group;
  } KeyShareHelloRetryRequest;

selected_group:服务器打算协商的都支持的组,准备为请求重试ClientHello/KeyShare。

在HelloRetryRequest中接收到此扩展时,客户端必须验证:
1、selected_group字段对应于在原始ClientHello的supported_groups扩展中提供的组;
2、selected_group字段与原始ClientHello中的key_share扩展中提供的组不对应。

       如果这些检查中的任一个失败,则客户端必须用illegal_parameter警报来中止握手。 否则,当发送新的ClientHello时,客户端必须用仅包含新KeyShareEntry的扩展替换原来的key_share扩展,新的扩展对应于触发HelloRetryRequest的selected_group字段中指示的组。
       在ServerHello中,此扩展的extension_data字段包含KeyShareServerHello值:

   struct {
      KeyShareEntry server_share;
  } KeyShareServerHello;

server_share:一个跟客户端共享的在同一个组中的KeyShareEntry值。

        如果使用(EC)DHE密钥协商,服务器在ServerHello中只提供一个KeyShareEntry。 该值必须与服务器为协商密钥交换选择的客户端提供的KeyShareEntry值在同一组。服务器不得为supported_groups扩展中指定的任何组发送KeyShareEntry,并且在使用psk_ke的PskKeyExchangeMode时不得发送KeyShareEntry。如果使用(EC)DHE密钥协商,并且客户端收到包含key_share扩展的HelloRetryRequest,客户端必须验证ServerHello中选择的NamedGroup与HelloRetryRequest中的相同,否则必须以illegal_parameter警报中止握手。

4.2.8.1. Diffie-Hellman参数

       客户端和服务器的Diffie-Hellman [DH76]参数都编码在KeyShare结构中的KeyShareEntry的opaque key_exchange字段中。opaque值包含指定组(参见[RFC7919]的组定义)的Diffie-Hellman公共值(Y = g ^ X mod p),编码为大端字节序整数,并用填充 0到左侧至p字节。
       注意:对于给定的Diffie-Hellman组,填充使所有公钥长度相同。
       对端应该通过确保1<Y<p-1来验证对方的公钥Y。此检查确保对端在行为正常,并且不强制本地系统进入小型组。

4.2.8.2. ECDHE参数

        客户端和服务器的ECDHE参数都编码在KeyShare结构中KeyShareEntry的opaque key_exchange字段中。
        对于secp256r1,secp384r1和secp521r1,是以下结构体的序列化值:

   struct {
      uint8 legacy_form = 4;
      opaque X[coordinate_length];
      opaque Y[coordinate_length];
  } UncompressedPointRepresentation;

          X和Y分别是X和Y值的网络序二进制表示。由于没有内部长度标记,因此每个数字占用曲线参数隐含的字节数。 对于P-256,这意味着X和Y分别使用32字节,如果需要,则在左侧填充零。对于P-384,分别占用48字节,对于P-521,各占用66字节。
          对于曲线secp256r1,secp384r1和secp521r1,对端必须通过确保该点是椭圆曲线上的有效点来验证彼此的公共值Y。相应的验证程序在[X962]的4.3.7中定义,或者在[KEYAGREEMENT]的5.6.2.6中定义。 该过程由三个步骤组成:
(1)验证Q不是无穷大点(O),
(2)验证Q =(x,y)中x和y两个整数都在正确的间隔,
(3)确保(x,y)是椭圆曲线方程的正确解。对于这些曲线,实现者不需要验证正确子组中的成员。

        对于X25519和X448,公共值的内容是[RFC7748]中定义的相应功能的字节串输入和输出:X25519是32字节,X448是56字节。
        注意: 1.3之前版本的TLS允许 point format 协商;TLS 1.3删除了此功能,从而使每个曲线有一个point format。

4.2.9. Pre-Shared Key交换模式

        为了使用PSK,客户端还必须发送一个psk_key_exchange_modes扩展。 此扩展的意思是客户端仅支持使用这些模式的PSK,这限制了在这个ClientHello中提供的PSK的使用以及服务器通过NewSessionTicket提供的PSK的使用。
        如果客户端提供了一个pre_shared_key扩展,也必须提供一个psk_key_exchange_modes扩展。 如果客户端提供了pre_shared_key,但没提供 psk_key_exchange_modes,服务器必须中止握手。服务器不得选择客户端未给出的密钥交换模式。此扩展还限制PSK恢复使用的模式。服务器不应发送与通告模式不兼容的NewSessionTicket,但是如果服务器这样做,影响将只是客户端尝试恢复会失败。
        服务器不得发送psk_key_exchange_modes扩展。

  enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;
  struct {
      PskKeyExchangeMode ke_modes<1..255>;
  } PskKeyExchangeModes;

psk_ke: PSK-only密钥建立。 在这种模式下,服务器不得提供key_share值。
psk_dhe_ke: PSK和(EC)DHE的秘钥建立。 在这种模式下,客户端和服务器必须提供key_share值(见4.2.8)。

        任何将来分配的值必须确保传输协议消息明确识别服务器选择的模式。目前这由ServerHello中的key_share表示。

4.2.10. Early Data Indication

        当使用PSK时,客户端可以在第一个消息中发送应用数据。 如果客户端选择这样做,就必须提供early_data扩展和pre_shared_key扩展。
       此扩展的extension_data字段包含EarlyDataIndication值。

  struct {} Empty;
  struct {
      select (Handshake.msg_type) {
          case new_session_ticket:   uint32 max_early_data_size;
          case client_hello:         Empty;
          case encrypted_extensions: Empty;
      };
  } EarlyDataIndication;

       max_early_data_size字段的使用见4.6.1。
       0-RTT参数(版本号、对称密码套件、ALPN协议[RFC7301]等)是使用PSK的关联值。对于外部配置的PSK,关联值与秘钥一起配置。对于通过NewSessionTicket消息确定的PSK,关联值是在确定PSK的连接里协商的。用于加密早期数据的PSK必须是客户端pre_shared_key扩展中列出的第一个PSK。
       对于通过NewSessionTicket提供的PSK,服务器必须验证所选PSK标识的 ticket 生存期(PskIdentity.obfuscated_ticket_age 模 2 ^ 32中减去ticket_age_add)在从ticket开始使用的小时间范围内(见第8章)。如果不是,服务器应该继续握手,但拒绝0-RTT,并且不应该采取任何假定该ClientHello是全新的其他操作。
       在第一个消息中发送的0-RTT消息与其他消息(握手和应用程序数据)中发送的相应消息具有相同(加密)的内容类型,但受到不同密钥的保护。在收到服务器的Finished消息后,如果服务器已接收到早期数据,则会发送EndOfEarlyData消息以指示密钥更改。该消息使用0-RTT流量密钥加密。
        接收early_data扩展的服务器必须以三种方式之一进行操作:

  • 忽略扩展并返回常规的1-RTT响应。然后,服务器忽略早期数据并尝试使用握手流量秘钥解密收到的数据,忽略解密失败的数据(直到配置的max_early_data_size)。一旦数据成功解密,则被当做客户端第二个消息的开始,服务端当做普通1-RTT握手继续处理。
  • 通过响应HelloRetryRequest请求客户端发送另一个ClientHello。 客户端不得在其后续ClientHello中包含early_data扩展。 然后,服务器跳过外部内容类型application_data的所有记录(表示被加密)来忽略早期数据,直到配置的max_early_data_size长度。
  • 在EncryptedExtensions中返回自己的early_data扩展,表示它打算处理早期的数据。 服务器不可能只接受早期数据消息的一部分。 即使服务器发送接收早期数据的消息,但是实际的早期数据本身可能已经在服务器生成此消息时发送了。

为了接受早期数据,服务器必须先接受了PSK密码套件并且选择了客户端的pre_shared_key扩展中提供的第一个密钥。此外,必须验证以下值与选择的PSK相关联:

  • TLS版本号。
  • 选择的密码套件。
  • 选择的ALPN协议 [RFC7301](如果有)。

           这些要求是使用相关PSK执行1-RTT握手的要求的超集。对于外部配置的PSK,关联值(associated values)与秘钥一起提供。对于通过NewSessionTicket消息确定的PSK,关联值是通过连接协商的。
         未来的扩展必须定义它们与0-RTT的交互。
        如果任一上述检查失败,服务器不得使用扩展进行响应,并且必须使用上面前两种机制之一丢弃所有第一个报文中的数据(回退到1-RTT或2-RTT)。 如果客户端尝试进行0-RTT握手,但是服务器拒绝,则服务器通常没有0-RTT保护密钥,必须使用试用解密(使用1-RTT握手密钥或在HelloRetryRequest的情况下通过寻找cleartext ClientHello)找到第一个非0-RTT消息。
        如果服务器选择接受early_data扩展,那么在处理早期数据时,它必须遵守与所有记录相同的错误处理要求。 具体来说,如果服务器在接受early_data扩展后无法解密任何0-RTT记录,则必须根据5.2使用bad_record_mac警报终止连接。
       如果服务器拒绝early_data扩展,则客户端应用程序可以在握手完成后重新发送早期数据。 请注意,早期数据的自动重新传输可能导致关于连接状态不正确的假设。 例如,当协商的连接选择与早期数据不同的ALPN协议时,应用程序可能需要构建不同的消息。 类似地,如果早期数据假定任何连接状态,则握手完成后可能发送错误。
      TLS实现不应自动重新发送早期数据;应用程序能够更好地决定重新传输是否合适。 除非协商的连接选择相同的ALPN协议,否则TLS实现不得自动重新发送早期数据。

4.2.11. PSK扩展

       pre_shared_key扩展用于协商PSK密钥建立相关联握手使用的预共享密钥标识。
      此扩展的extension_data字段包含PreSharedKeyExtension值:

  struct {
      opaque identity<1..2^16-1>;
      uint32 obfuscated_ticket_age;
  } PskIdentity;

  opaque PskBinderEntry<32..255>;

  struct {
      PskIdentity identities<7..2^16-1>;
      PskBinderEntry binders<33..2^16-1>;
  } OfferedPsks;

  struct {
      select (Handshake.msg_type) {
          case client_hello: OfferedPsks;
          case server_hello: uint16 selected_identity;
      };
  } PreSharedKeyExtension;

Identity:秘钥标签。例如,附录B.3.4中定义的ticket,或外部配置的psk标签。
obfuscated_ticket_age:密钥生存时间的混淆版本。4.2.11.1描述了怎样通过NewSessionTicket消息建立的标识生成此值。对于外部配置的标识,应该使用0的obfuscated_ticket_age,服务器必须忽略该值。
Identities:客户端想要与服务器协商的标识列表。如果与early_data扩展一起发送(4.2.10),第一个标识是用于0-RTT数据。
Binders:一系列HMAC值,每个标识值一个,并且以相同顺序排列,计算过程如下所述。
selected_identity:服务器选择的标识,以客户端列表中的标识表示为(0-based)的索引。

        每个PSK与一个哈希算法相对应。对于通过 ticket机制建立的PSK(4.6.1节),是建立ticket的连接上的 KDF Hash算法。对于外部配置的PSK,当PSK建立时,必须设置哈希算法,或者没指定算法时默认为SHA-256。 服务器必须确保选择兼容的PSK(如果有的话)和密码套件。
        TLS 1.3之前的版本中,服务器名字标识(Server Name Identification,SNI)值需要与session相对应([RFC6066])中第3章),服务器需要强制SNI值与握手恢复中指定的匹配session相对应。然而,现实中使用提供的SNI值中的哪一个,实现并不一致,这导致客户端事实上强制执行一致性要求。在TLS 1.3中,SNI值总是在恢复握手中指定,服务器没必要将SNI值与ticket关联。但客户端应该与PSK一起存储SNI来满足4.6.1中的要求。
       实现需注意:当会话恢复是PSK主要应用场景时,实现PSK/密码套件匹配要求的最直接方法是先协商密码套件,然后排除任何不兼容的PSK。任何未知的PSK(如不在PSK数据库中或者以未知秘钥加密)应该忽略。如果没有可接受的PSK,服务器可能的话应该执行non-PSK握手。如果前向兼容很重要,客户端的外部配置PSK应该决定密码套件选择。
        在接受PSK密钥建立之前,服务器务必验证相应的binder值(4.2.11.2)。如果此值不存在或未验证,则服务器必须中止握手。服务器不应该尝试验证多个binder,而是应该选择单个PSK并且仅验证对应于该PSK的binder。该要求的安全原理见8.2和附录E.6。为了接受PSK密钥建立,服务器发送pre_shared_key扩展来指示选择的标识。
      客户端必须验证服务器的selected_identity是否在客户端提供的范围内,服务器选择了包含与PSK关联哈希的加密套件,并且如果ClientHello的psk_key_exchange_modes扩展需要,服务器应该发送key_share扩展。 如果这些值不一致,客户端必须使用illegal_parameter警报中止握手。
       如果服务器提供了early_data扩展, 客户端必须验证服务器的selected_identity是否为0。如果返回任何其他值,客户端必须使用illegal_parameter警报中止握手。
       pre_shared_key扩展必须是ClientHello中的最后一个扩展(这有助于如下所述的实现)。 服务器必须检查它是最后一个扩展,否则以illegal_parameter警报握手失败。

4.2.11.1. Ticket Age

         客户端对ticket生存时间的看法是收到NewSessionTicket消息后的时间。 客户端不得尝试使用ticket生存时间大于ticket_lifetime值的ticket。每个PskIdentity 的obfuscated_ticket_age字段包含了ticket生存时间的混淆版本,以毫秒为单位,加上ticket中包含的ticket_age_add值(4.6.1),模2^32。这个添加可以防止被动观察者关联连接,除非tiket被重复使用。

         请注意, NewSessionTicket消息中的ticket_lifetime字段的单位是秒,但obfuscated_ticket_age的单位是毫秒。因为ticket生存期被限制在一周之内,32位足以代表任何可信的生存期,即使是以毫秒为单位。


4.2.11.2. PSK Binder
        PSK binder值绑定了PSK和当前握手,并且绑定生成PSK的握手(如果通过NewSessionTicket消息)和当前握手。binder列表中的每个条目被计算为直到(并包括)PreSharedKeyExtension.identities字段的ClientHello部分(包括握手首部)上的HMAC(transcript hash,见4.4.1)。也就是说,它包括所有ClientHello,但不包括binder列表本身。消息的长度字段(包括总长度,扩展块长度和pre_shared_key扩展长度)都是按照正确长度的binder来设置的。
PskBinderEntry以Finished消息(4.4.4)相同方式计算,但是BaseKey是提供相应PSK的密钥派生表派生的binder_key(7.1)。
       如果握手包括HelloRetryRequest,则初始ClientHello和HelloRetryRequest与新的ClientHello一起被包括在副本中。例如,如果客户端发送ClientHello1,则其binder将如下计算:

Transcript-Hash(Truncate(ClientHello1))

其中Truncate()从ClientHello中移除binder列表。

如果服务器响应HelloRetryRequest,然后客户端然后发送ClientHello2,其binder将通过计算:

  Transcript-Hash(ClientHello1,
                  HelloRetryRequest,
                  Truncate(ClientHello2))

完整的ClientHello1/ClientHello2包括在所有其他握手哈希计算中。注意在第一次发送中,Truncate(ClientHello1)是直接哈希的,但在第二次发送中ClientHello1先哈希,然后作以message_hash消息注入,如4.4.1所述。

4.2.11.3. 处理顺序

        客户端接收服务器的Finished之前都可以stream 0-RTT数据,然后发送EndOfEarlyData消息,接着是剩下的握手。 为了避免死锁,当接受early_data时,服务器必须处理客户端的ClientHello,然后立即发送消息,而不是在发送ServerHello前等待客户端的EndOfEarlyData消息。

4.3. 服务端参数

          来自服务器的接下来两条消息 EncryptedExtensions和 CertificateRequest,包含来自决定其余握手的服务器的信息。 这些消息使用从server_handshake_traffic_secret派生的密钥加密。

4.3.1. 加密扩展(Encrypted Extensions)

          在所有握手中,服务器必须在ServerHello消息之后立即发送EncryptedExtensions消息。 这是使用server_handshake_traffic_secret派生密钥加密的第一条消息。

         EncryptedExtensions消息包含可以加密的扩展,即,不需要建立加密上下文但不与各个证书相关联的扩展。 客户端必须检查EncryptedExtensions是否存在任何禁止的扩展,如果发现任何此类扩展,必须用illegal_parameter警报中止握手。

这个消息的结构:

  struct {
      Extension extensions<0..2^16-1>;
  } EncryptedExtensions;

Extensions:扩展列表。更多信息见4.2的表格。

4.3.2. 请求证书(Certificate Request)

        使用证书进行身份验证的服务器可以选择向客户端请求证书。如果发送了这个消息,必须在EncryptedExtensions之后。

       此消息的结构:

  struct {
      opaque certificate_request_context<0..2^8-1>;
      Extension extensions<2..2^16-1>;
  } CertificateRequest;

certificate_request_context:一个不透明的字符串,用于表示证书请求,并在客户端的Certificate消息中回复。 certificate_request_context在此连接的范围内必须是唯一的(从而防止客户端CertificateVerify消息的重放)。该字段应为零长度,除非用于4.6.2节中描述的post-handshake身份验证交互。当请求post-handshake认证时,服务器应该使上下文对客户端不可预测(比如随机生成),从而防止临时访问客户端私钥的攻击者预先计算出有效的CertificateVerify消息。

Extensions:描述正在请求的证书参数的扩展集。必须指定signature_algorithms扩展名,如果此消息定义了其他扩展也可以选择性包含。客户端必须忽略不认识的扩展。

        在TLS的先前版本中,CertificateRequest消息携带服务器接受的签名算法和证书授权列表。在TLS 1.3中,前者通过发送signature_algorithms和可选的signature_algorithms_cert扩展来表示。后者通过发送certificate_authorities扩展来表示(见4.2.4)。
      使用PSK认证的服务器不得在主握手中发送CertificateRequest消息,虽然可以在post-handshake认证中发送(见4.6.2),但客户端要已经发送了post_handshake_auth扩展(见4.2.6)。

4.4. 认证消息(Authentication Messages)

       如第2章所述,TLS通常使用一组消息来进行身份验证,密钥确认和握手完整性:Certificate,CertificateVerify和Finished。 (PSK binder也以类似的方式执行密钥确认。) 这三个消息总是在握手中的最后发送。 Certificate和CertificateVerify消息仅在某些情况下发送,如下所定义。Finished的消息总是作为Authentication Block的一部分发送。这些消息使用[sender]_handshake_traffic_secret派生的密钥加密。

认证消息的计算统一采用以下输入:

  • 要使用的证书和签名密钥。
  • 一个握手上下文,由要包含在transcript哈希中的一组消息组成。
  • 用于计算MAC密钥的Base Key。

基于这些输入,消息包含:
Certificate:用于认证的证书和链中的任何支持证书。请注意,基于证书的客户端身份验证在PSK握手流中不可用(包括0-RTT)。
CertificateVerify:Transcript-Hash(Handshake Context, Certificate)的签名。
Finished:Transcript-Hash值的MAC(握手上下文+证书+证书验证),使用Base Key派生的MAC秘钥。

下表定义了每个场景的握手上下文和MAC Base Key:

4.4.1. Transcript Hash

        TLS的很多加密算法使用transcript hash。这个值是通过对每个包含的握手消息进行哈希串联计算出来的,包括携带握手消息类型和长度字段的握手消息头,但不包括记录层头。 即:

Transcript-Hash(M1, M2, ... Mn) = Hash(M1 || M2 || ... || Mn)

         作为这个一般规则的例外,当服务器用HelloRetryRequest响应一个ClientHello时,ClientHello1的值会被一个特殊的合成握手消息代替,握手类型为message_hash,包含Hash(ClientHello1)。 即:


  Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =
      Hash(message_hash ||        /* Handshake type */
           00 00 Hash.length  ||  /* Handshake message length (bytes) */
           Hash(ClientHello1) ||  /* Hash of ClientHello1 */
           HelloRetryRequest  || ... || Mn)

        这种结构的原因是允许服务器只在cookie中存储ClientHello1的哈希值,而不用导出整个中间哈希状态,从而进行无状态的HelloRetryRequest(4.2.2)。
        为了具体化,transcript hash总是取自以下握手消息序列,从第一个ClientHello开始,只包括那些被发送的消息:ClientHello, HelloRetryRequest, ClientHello, ServerHello, EncryptedExtensions, server CertificateRequest, server Certificate, server CertificateVerify, server Finished, EndOfEarlyData, client Certificate, client CertificateVerify, client Finished。
一般来说,实现者可以根据协商好的哈希值保持一个运行中的transcript hash值来实现transcript。 但请注意,后续的post-handshake认证不包括,只包含到主握手结束的消息。

4.4.2. Certificate

       该消息将端点的证书链传递给对端。
       每当约定的密钥交换方法使用证书进行认证时,服务器必须发送一条Certificate消息(这包括本文中定义的所有密钥交换方法,PSK除外)。

       只有服务器通过CertificateRequest消息请求客户端认证时,客户端才必须发送Certificate消息(4.3.2)。 如果服务器请求客户端认证,但客户端没有合适的证书,则必须发送一个不包含证书的Certificate消息(即certificate_list字段长度为0)。 无论Certificate消息是否为空,都必须发送一个Finished消息。

       这个消息的格式:


      enum {
          X509(0),
          RawPublicKey(2),
          (255)
      } CertificateType;

      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;

      struct {
          opaque certificate_request_context<0..2^8-1>;
          CertificateEntry certificate_list<0..2^24-1>;
      } Certificate;

certificate_request_context:如果该消息是对CertificateRequest的回应,则为该消息中的certificate_request_context的值。 否则(在服务器认证的情况下),该字段长度为零。

certificate_list:CertificateEntry结构的序列(链),每个结构包含一个证书和一组扩展。

extensions:CertificateEntry的一组扩展值。 扩展格式在4.2中定义。 目前服务器证书的有效扩展包括OCSP Status扩展[RFC6066]和SignedCertificateTimestamp扩展[RFC6962];未来也可以为该消息定义扩展。 服务器的Certificate消息中的扩展必须与ClientHello消息的扩展相对应。 客户端的Certificate消息中的扩展必须与服务器的 CertificateRequest 消息中的扩展相对应。 如果一个扩展用于整个链,它应该包含在第一个CertificateEntry 中。

        如果对应的证书类型扩展(server_certificate_type或client_certificate_type)没有在EncryptedExtensions中协商,或者协商为X.509证书类型,那么每个CertificateEntry都包含一个DER编码的X.509证书。 发送者的证书必须在列表中的第一个CertificateEntry 中。 后面的每个证书都应该直接认证紧邻它前面的证书。 因为证书验证要求trust anchors是独立分发的,所以指定trust anchors的证书可以从链中省略,前提是支持的对端已知拥有任何省略的证书。

      注意:在TLS 1.3之前,certificate_list的排序要求每个证书都要对紧接在它前面的证书进行认证;但是,一些实现允许一些灵活性。 服务器有时会为了过渡目的同时发送一个当前的和过时的中间文件,还有一些只是配置不正确,但这些情况还是可以被正确验证。 为了实现最大的兼容性,所有的实现都应该准备好处理任何TLS版本中潜在的无关证书和任意的顺序,但最终实体证书必须放在第一位。

      如果协商为RawPublicKey证书类型,那么certificate_list必须不超过一个CertificateEntry,其中包含[RFC7250]第3节中定义的ASN1_subjectPublicKeyInfo值。
      OpenPGP证书类型[RFC6091]不得用于TLS 1.3。
      服务器的certificate_list必须是非空的。如果客户端没有合适的证书来响应服务器的认证请求,客户端将发送一个空的证书列表。

4.4.2.1. OCSP状态和 SCT扩展

       [RFC6066]和[RFC6961]提供了扩展来协商服务器向客户端发送OCSP响应。 在TLS 1.2及之前版本中,服务器用一个空的扩展来表示协商这个扩展,OCSP信息携带在CertificateStatus消息中。 在TLS 1.3中,服务器的OCSP信息在包含相关证书的CertificateEntry中的扩展中。 具体来说,服务器的status_request扩展的主体必须是[RFC6066]中定义的CertificateStatus结构,其解释在[RFC6960]中定义。

       注意:status_request_v2扩展[RFC6961]已经废弃。TLS 1.3服务器在处理ClientHello消息时,不能根据status_request_v2的存在或其中的信息采取行动,尤其是不能在EncryptedExtensions、CertificateRequest 或 Certificate 消息中发送此扩展。TLS 1.3服务器必须能够处理包含status_request_v2的ClientHello消息,因为使用早期协议版本的客户端发送可能想要使用。

        服务器可以通过在CertificateRequest消息中发送一个空的status_request扩展,要求客户机提供OCSP响应和它的证书。如果客户端选择发送OCSP响应,其status_request扩展的主体必须是[RFC6066]中定义的CertificateStatus结构。
同样,[RFC6962]提供了一种机制,在TLS 1.2及以下版本中,服务器可以将签名证书时间戳(SCT,Signed Certificate Timestamp)作为ServerHello中的扩展发送。 在TLS 1.3中,服务器的SCT信息在CertificateEntry的扩展中。

4.4.2.2. 服务端证书选择

服务器发送的证书遵循以下规则:

  • 证书类型必须是X.509v3[RFC5280],除非另有显式协商(如[RFC7250])。
  • 服务器的终端实体证书的公钥(和相关的限制)必须与客户的signature_algorithms扩展(目前是RSA、ECDSA或EdDSA)中选择的认证算法兼容。
  • 证书必须允许使用秘钥进行签名(即如果有Key Usage扩展,digitalSignature位必须被设置),签名方案在客户端的signature_algorithms或signature_algorithms_cert扩展中指明(4.2.3)。
  • server_name[RFC6066]和certificate_authorities扩展用来指导证书的选择。由于服务器可能需要server_name扩展,客户端应该可用情况下发送此扩展。

服务器提供的所有证书必须由客户端提供的签名算法签名(如果客户端能够提供这样的链,见4.2.3)。 自签证书或预期为trust anchors的证书不作为链的一部分进行验证,因此可以用任何算法进行签名。
如果服务器不能产生一个只通过指定的支持算法进行签名的证书链,那么它应该向客户端发送一个自己选择的包含不知道客户端是否支持的算法证书链来继续握手。这个fallback链一般不应使用已淘汰的SHA-1散列算法,但如果客户端通告允许的话可以使用,否则不得使用。
如果客户端不能使用提供的证书构建一个可接受的链,并决定放弃握手,那么它必须用一个适当的certificate-related警报(默认情况下为unsupported_certificate;更多信息见6.2)来放弃握手。
如果服务器有多个证书,它就会根据上述标准(除此之外还有其他标准,如传输层端点、本地配置和偏好)选择其中一个。

4.4.2.3. 客户端证书选择

客户端发送的证书遵循以下规则:

  • 证书类型必须是X.509v3[RFC5280],除非另有明确协商(如[RFC7250])。
  • 如果CertificateRequest消息中存在certificate_authorities扩展,那么证书链中至少有一个证书应该是由所列的CA签发的。
  • 证书必须使用可接受的签名算法来签署,如4.3.2所述。需要注意的是,这放宽了以前版本的TLS中对证书签名算法的限制。
  • 如果CertificateRequest消息包含一个非空的oid_filters扩展,那么终端实体证书必须与客户端识别的扩展名OID匹配,如4.2.5所述。

4.4.2.4. 接收Certificate消息

一般来说,详细的证书验证程序不在TLS的范围内(见[RFC5280])。 本节提供了特定于TLS的要求。
如果服务器提供了空的Certificate消息,客户端必须用decode_error警告中止握手。
如果客户端没有发送任何证书(比如发送一个空的Certificate消息),服务器可以自行决定是在没有客户端认证的情况下继续握手,还是用certificate_required 警告中止握手。 另外,如果证书链的某些方面是不能接受的(例如,不是由已知的、受信任的CA签发的),服务器可以自行决定继续握手(考虑到客户端未被认证)或中止握手。
任何端点收到任何需要使用MD5哈希签名算法来验证的证书,都必须以 bad_certificate警告来中止握手。 SHA-1已废弃,建议任何端点收到任何需要使用SHA-1散列签名算法来验证的证书时,应当以bad_certificate警告中止握手。 为明确起见,这意味着端点可以接受这些算法,用于自签或trust anchors的证书。
建议所有端点尽快过渡到SHA-256或更高版本,以保持与目前正在逐步取消SHA-1支持的实施方案的互操作性。
请注意,包含一种签名算法密钥的证书可以使用不同的签名算法进行签名(例如,用ECDSA密钥签名的RSA密钥)。

4.4.3. 证书验证(CertificateVerify)

该消息用于明确证明端点拥有与其证书相对应的私钥。CertificateVerify消息也为截至当前的握手提供完整性。服务器在通过证书进行验证时必须发送此消息,客户端在通过证书进行验证时必须发送此消息。客户端在通过证书进行验证时必须发送此消息(如Certificate消息非空时)。该消息必须紧接在Certificate消息之后发送,并在Finished消息之前出现。

   Structure of this message:
      struct {
          SignatureScheme algorithm;
          opaque signature<0..2^16-1>;
      } CertificateVerify;

算法字段指定所使用的签字算法(该类型的定义见4.2.3)。 签名是使用该算法的数字签名。签名的内容是4.4.1所述的哈希输出,即:

   Transcript-Hash(Handshake Context, Certificate)

然后,数字签名的计算是在下列各项的连接上进行的:

  •  一个由字节32(0x20)组成的字符串,重复64次。
  • context字符串
  •  一个0字节,作为分隔符。
  • 需要签署的内容

这个结构的目的是为了防止对以前版本的TLS的攻击,在这种情况下,ServerKeyExchange格式意味着攻击者可以获得一个带有32字节前缀(ClientHello.random)的消息签名。 初始的64字节填充会清除这个前缀和服务器控制的ServerHello.random。
服务器签名的context字符串是"TLS 1.3, server CertificateVerify“。 客户端签名的context字符串是”TLS 1.3, client CertificateVerify"。 它用来区分不同环境下的签名,有助于防止潜在的跨协议攻击。
例如,如果transcript哈希是32字节的01(这个长度对于SHA-256来说是有意义的),那么服务器CertificateVerify的数字签名所覆盖的内容就是:

  2020202020202020202020202020202020202020202020202020202020202020
  2020202020202020202020202020202020202020202020202020202020202020
  544c5320312e332c207365727665722043657274696669636174655665726966
  79
  00
  0101010101010101010101010101010101010101010101010101010101010101

在发送方,计算CertificateVerify消息的签名字段的输入为:

  • 数字签名所涵盖的内容
  • 之前信息中发送的证书所对应的私人签名密钥。

如果CertificateVerify消息是由服务器发送的,签名算法必须是客户端 signature_algorithms扩展中提供的算法,除非没有不支持的算法就不能产生有效的证书链(见4.2.3)。
如果是客户端发送的,签名中使用的签名算法必须是CertificateRequest消息中 signature_algorithms 扩展中supported_signature_algorithms字段中的算法之一。
此外,签名算法必须与发送者的终端实体证书中的秘钥兼容。RSA签名必须使用RSASSA-PSS算法,不管RSASSA-PKCS1-v1_5算法是否出现在signature_algorithms中。 SHA-1算法不得用于任何CertificateVerify消息的签名中。
本规范中的所有 SHA-1 签名算法只用于传统证书,而不适用于CertificateVerify签名。

CertificateVerify消息的接收方必须验证签名字段。 验证过程的输入是:

  • 数字签名所涵盖的内容
  • 相关Certificate信息中找到的终端实体证书中包含的公钥。
  • CertificateVerify消息的签名中收到的数字签名。

如果验证失败,接收方必须用decrypt_error警报终止握手。

4.4.4. Finished

Finished消息是Authentication Block中的最后一条消息,它对提供握手和密钥的认证至关重要。
Finished消息的接收者必须验证其内容是否正确,如果不正确必须用decrypt_error警报终止连接。

         一旦一方发送了Finished消息,并且收到并验证了对端的Finished消息,它就可以开始通过连接发送和接收应用数据。 有两种设置允许它在收到对端的Finished之前发送数据:

  1. 发送0-RTT数据的客户端,如4.2.10所述。
  2. 服务器在第一次发送后可以发送数据,但由于握手还没有完成,所以既不能保证对端的身份,也不能保证它的活性(如ClientHello可能已经被重放)。

          用于计算Finished消息的密钥是由4.4中定义的Base Key使用HKDF计算的(见7.1)。具体来说:

finished_key =
       HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)

   Structure of this message:
      struct {
          opaque verify_data[Hash.length];
      } Finished;

   verify_dataj
      verify_data =
          HMAC(finished_key,
               Transcript-Hash(Handshake Context,
                               Certificate*, CertificateVerify*))

      * Only included if present.

       HMAC[RFC2104]采用Hash算法进行握手。如上所述,HMAC的输入一般可以通过运行哈希来实现,如此时只需要握手哈希。
       在以前的TLS版本中,verify_data总是12字节长。在TLS 1.3中,是用于握手的Hash的HMAC输出大小。
        注意:警报和任何其他非握手记录类型不属于握手消息,不包括在哈希计算中。
        在Finished消息之后的任何记录必须按照7.2所述的适当应用流量密钥进行加密。特别是,这包括服务器响应客户端Certificate和CertificateVerify消息而发送的任何警报。

4.5. End of Early Data

struct {} EndOfEarlyData;

如果服务器在EncryptedExtensions中发送了early_data扩展,那么客户端必须在收到服务器Finished后发送EndOfEarlyData消息。如果服务器没有在EncryptedExtensions中发送early_data扩展,那么客户端不得发送EndOfEarlyData消息。该消息表示所有的0-RTT application_data消息(如果有的话)已经传输完毕,之后的数据由握手流量密钥保护。服务器不得发送此消息,客户端收到此消息必须以unexpected_message警报终止连接。这个消息是由client_early_traffic_secret衍生的密钥加密的。

4.6. Post-Handshake消息

TLS还允许在主握手之后发送其他消息。这些消息使用握手内容类型,并由合适的应用流量密钥进行加密。

4.6.1. NewSessionTicket消息

在收到客户端Finished消息后,服务端任何时候都可以发送NewSessionTicket消息。 该消息在ticket值和从恢复主秘钥(resumption_master_secret)中导出的秘密PSK之间建立了对应关系(一一对应,见第7节)。 客户端可以通过在ClientHello(4.2.11)中的pre_shared_key 扩展中包含ticket值,在未来的握手中使用这个PSK。 服务器可以在一个连接上发送多个ticket,可以是紧接着发送,也可以是在特定事件之后(见附录C.4)。例如,服务器可能会在post-handshake认证之后发送一个新的ticket,以便封装额外的客户端认证状态。 多tikcet对客户端有多种用途,包括:

  • 打开多个并行HTTP连接。
  • 通过(例如)Happy Eyeballs [RFC8305]或相关技术跨接口和地址族执行连接竞速。

       任何ticket必须只用建立原始连接时使用的KDF哈希算法相同的密码套件来恢复。
       只有当新的SNI值对原始会话中提交的服务器证书有效时,客户端才必须恢复;只有当SNI值与原始会话中使用的SNI值匹配时,客户端才应该恢复。后者是一种性能优化:通常情况下,没有理由期望一个证书所覆盖的不同服务器能够接受对方的ticket;因此,在这种情况下尝试恢复会浪费一张单次使用的ticket。 如果提供了这样的指示(外部或任何其他方式),客户端可以用不同的SNI值恢复。
        如果恢复时向应用程序传递SNI值,实现必须使用恢复ClientHello中发送的值,而不是上一个会话中发送的值。注意,如果服务器实现拒绝所有不同SNI值的PSK标识,则这两个值总是相同的。
        注意:虽然恢复主秘钥(resumption_master_secret)取决于客户端的第二次发送,但不要求客户端认证的服务器可以独立计算transcript的剩余部分,然后在发送Finished后立即发送NewSessionTicket,而不是等待客户端Finished。 这可能适用于这样的情况:例如,客户端预计将并行打开多个TLS连接,并将从减少恢复握手的开销中受益。

  struct {
      uint32 ticket_lifetime;
      uint32 ticket_age_add;
      opaque ticket_nonce<0..255>;
      opaque ticket<1..2^16-1>;
      Extension extensions<0..2^16-2>;
  } NewSessionTicket;

ticket_lifetime:表示从发布ticket时间开始的32位无符号整数,以秒为单位,网络序。 服务器不得使用任何大于604800秒(7天)的值。 值为零时表示ticket应立即丢弃。无论ticket_lifetime多少,客户端都不得将ticket缓存超过 7 天,可以根据本地策略提前删除ticket。 服务器可以在比ticket_lifetime 更短的时间内将tiket视为有效。

ticket_age_add:一个安全生成的随机32位值,用于掩盖客户端pre_shared_key 扩展中的ticket年龄。 此值加上客户端的tikcet年龄,模2^32,得到客户端传输的值。服务器必须为每次发送的ticket生成一个新的值。

ticket_nonce:每个ticket对应一个值,使ticket在这个连接上唯一。

Ticket:作为PSK标识的ticket值。ticket本身是一个不透明的标签。 它可以是一个数据库查询键,也可以是一个自加密和自认证的值。

Extensions:ticket的一组扩展。扩展格式在4.2中定义。客户端必须忽略不认识的扩展。

目前为NewSessionTicket定义的唯一扩展是early_data,表示该ticket可用于发送0-RTT数据(4.2.10)。 包含以下值:
max_early_data_size:使用该ticket时,客户端允许发送的0-RTT数据的最大数量,单位为字节。 大小只包含应用数据有效载荷(如明文,但不包括填充或内部内容类型字节)。 服务器接收到超过max_early_data_size字节的0-RTT数据时,应该用unexpected_message警报终止连接。 请注意,由于缺乏加密材料而拒绝早期数据的服务器将无法区分padding和内容,所以客户端不要依赖能够在早期数据记录中发送大量的padding。


ticket关联的PSK计算方式为:

   HKDF-Expand-Label(resumption_master_secret,
                    "resumption", ticket_nonce, Hash.length)

        因为每个NewSessionTicket消息的ticket_nonce值是不同的,所以每个ticket都会衍生出不同的PSK。
        请注意,原则上可以继续发行新的ticket,它可以无限期地延长最初从initial non-PSK握手(很可能与对端证书绑定)中派生的密钥材料的寿命。 建议实施者对这种密钥材料的总寿命进行限制;这些限制应考虑到对端证书的寿命、干预性撤销的可能性以及对端在线CertificateVerify签名后的时间。

4.6.2. 握手后认证(Post-Handshake Authentication)

当客户端发送了post_handshake_auth扩展(见4.2.6)后,服务器可以在握手完成后的任何时候通过发送CertificateRequest消息来请求客户端认证。 客户端必须用适当的Authentication消息来响应(见4.4)。 如果客户端选择认证,则必须发送Certificate、CertificateVerify和Finished。 如果客户端拒绝,则必须发送一个不包含证书的Certificate消息,然后是Finished。 客户端对给定响应的所有消息必须连续发送,中间不能有其他类型的消息。
客户端如果在没有发送post_handshake_auth扩展的情况下接收到CertificateRequest消息,必须发送一个unexpected_message的致命警告。

注意:由于客户端认证可能涉及到提示用户,服务器必须准备好一些延迟,包括在发送 CertificateRequest和接收响应之间收到任意数量的其他消息。 此外,如果客户端连续收到多个CertificateRequest,可能会以不同的顺序响应(certificate_request_context值允许服务器识别响应)。

4.6.3. 秘钥和初始化向量更新

        KeyUpdate握手消息用于指示发送方正在更新其发送的加密密钥。这个消息可以由任何一端在Finished消息后发送。在收到Finished消息之前收到KeyUpdate消息必须用unexpected_message警报来终止连接。在发送KeyUpdate消息后,发送方必须使用下一代密钥发送所有流量,这些密钥按照7.2的描述计算。在收到KeyUpdate消息后,接收方必须更新接收密钥。

  enum {
      update_not_requested(0), update_requested(1), (255)
  } KeyUpdateRequest;

  struct {
      KeyUpdateRequest request_update;
  } KeyUpdate;

request_update:表示KeyUpdate的接收者是否应该用自己的KeyUpdate来响应。如果接收到任何其他的值,必须用illegal_parameter警报来终止连接。

         如果request_update字段被设置为update_requested,那么接收方必须在发送下一个应用数据之前,发送一个自己的KeyUpdate,并将request_update设置为update_not_requested。 这个机制允许任何一方强制更新整个连接,但是会导致接收多个KeyUpdates的实现在沉默的时候用一个更新来响应。 请注意,在发送request_update设置为update_requested的 KeyUpdate和接收对端的KeyUpdate之间,可能会收到任意数量的消息,因为这些消息可能已经在路上。 然而,由于发送和接收密钥来自于独立的流量秘钥,因此保留接收流量秘钥并不会威胁到发送者更改密钥之前发送的数据的前向保密性。
       如果实现独立发送request_update设置为update_requested的KeyUpdate,并且它们在传输中交叉,那么每一方也会发送一个响应,结果就是每一方递增两代。
       发送方和接收方都必须用旧密钥加密他们的KeyUpdate消息。 此外,双方都必须强制要求在接受任何用新密钥加密的消息之前,收到用旧密钥的KeyUpdate。如果不这样做,可能会引发消息截断攻击。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值