ssl - 解释由 RFC6066 服务器名称指示定义的 SSL ClientHello SNI 消息扩展语法

标签 ssl tls1.2 rfc sni

<分区>


想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post .

关闭 3 年前

RFC6066server_name 类型的扩展中定义服务器名称指示。此扩展的 extension_data 字段应包含 ServerNameList,其中:

      struct {
          NameType name_type;
          select (name_type) {
              case host_name: HostName;
          } name;
      } ServerName;

      enum {
          host_name(0), (255)
      } NameType;

      opaque HostName<1..2^16-1>;

      struct {
          ServerName server_name_list<1..2^16-1>
      } ServerNameList;

最好能逐步解释这个数据结构。另外,这里是示例代码,可以找到here , 如何读取扩展数据:

private static List<SNIServerName> exploreSNIExt(ByteBuffer input,
        int extLen) throws IOException {

    Map<Integer, SNIServerName> sniMap = new LinkedHashMap<>();

    int remains = extLen;
    if (extLen >= 2) {     // "server_name" extension in ClientHello
        int listLen = getInt16(input);     // length of server_name_list
        if (listLen == 0 || listLen + 2 != extLen) {
            throw new SSLProtocolException(
                    "Invalid server name indication extension");
        }

        remains -= 2;     // 0x02: the length field of server_name_list
        while (remains > 0) {
            int code = getInt8(input);      // name_type
            int snLen = getInt16(input);    // length field of server name
            if (snLen > remains) {
                throw new SSLProtocolException(
                        "Not enough data to fill declared vector size");
            }
            byte[] encoded = new byte[snLen];
            input.get(encoded);

            SNIServerName serverName;
            switch (code) {
                case StandardConstants.SNI_HOST_NAME: // 0x00
                    if (encoded.length == 0) {
                        throw new SSLProtocolException(
                                "Empty HostName in server name indication");
                    }
                    serverName = new SNIHostName(encoded);
                    break;
                default:
                    serverName = new UnknownServerName(code, encoded);
            }
            // check for duplicated server name type
            if (sniMap.put(serverName.getType(), serverName) != null) {
                throw new SSLProtocolException(
                        "Duplicated server name of type "
                        + serverName.getType());
            }

            remains -= encoded.length + 3;  // NameType: 1 byte
            // HostName length: 2 bytes
        }
    } else if (extLen == 0) {     // "server_name" extension in ServerHello
        throw new SSLProtocolException(
                "Not server name indication extension in client");
    }

    if (remains != 0) {
        throw new SSLProtocolException(
                "Invalid server name indication extension");
    }

    return Collections.<SNIServerName>unmodifiableList(
            new ArrayList<>(sniMap.values()));
}

字节读取器:

private static int getInt16(ByteBuffer input) {
    return ((input.get() & 0xFF) << 8) | (input.get() & 0xFF);
}

Here是如何读取数据的好例子。例如,扩展类型是通过读取 2 个字节来定义的 - 那么另一个问题是 - 哪个 RFC 定义了它?

最佳答案

如果您已经有了实现它的源代码,还需要了解什么?

用于抽象模式的格式源自XDR但在每个 TLS 规范中都有具体定义,例如 3. Presentation Language 中的最后一个规范

所以如果我们一 block 一 block 地看:

  struct {
      NameType name_type;
      select (name_type) {
          case host_name: HostName;
      } name;
  } ServerName;

参见 https://www.rfc-editor.org/rfc/rfc8446#section-3.6 ,这定义了一个结构:

  • 名为“ServerName”
  • 其第一个组件称为name_type类型称为 NameType (稍后定义)
  • 其第二个也是最后一个组件称为name并且是一个变体(https://www.rfc-editor.org/rfc/rfc8446#section-3.8):它的值取决于 以前的 name_type内容。如果name_type具有的值(value) host_name , 那么第二个分量的值是类型 HostName (稍后定义)

下一步:

enum {
      host_name(0), (255)
} NameType;

参见 https://www.rfc-editor.org/rfc/rfc8446#section-3.5 , 这定义了一个只有一个可能值 ( 0 ) 的枚举,其别名是 host_name

(255)仅用于强制宽度(因此 0 到 255,因为值适合一个字节,此结构使用一个字节的空间),如规范中所述:

One may optionally specify a value without its associated tag to force the width definition without defining a superfluous element.

所以这意味着你在网络上使用 0,但如果你有 0,它就是编码 host_name在规范的其他部分。

  opaque HostName<1..2^16-1>;

https://www.rfc-editor.org/rfc/rfc8446#section-3.2我们有:

Single-byte entities containing uninterpreted data are of type opaque.

并且在 https://www.rfc-editor.org/rfc/rfc8446#section-3.4 , <>用于定义可变长度向量(或一维数组,或列表)。

所以 HostName是一个包含 1 到 216-1 个字节(不是元素)的向量,每个元素都是“不透明”类型,即一个字节。

请注意,在 RFC 中有关于 SNI 的进一步解释:

"HostName" contains the fully qualified DNS hostname of the server, as understood by the client. The hostname is represented as a byte string using ASCII encoding without a trailing dot.

  struct {
      ServerName server_name_list<1..2^16-1>
  } ServerNameList;

与第一个情况相同,但使用像上面一样的可变长度数组

  • 一个ServerNameList是一个结构
  • 唯一的元素是一个长度在 1 到 216-1 字节之间的可变数组
  • 这个数组的每个元素都是ServerName类型的, 如前所述

换句话说:

  • 一个ServerNameList structure 是一个元素列表,每个元素的类型都是 ServerName
  • 这个列表不能为空,因为它至少有 1 个字节的长度 (最多 216-1 字节)
  • 一个ServerName编码 name_type (只能是“host_name”,也就是值 0)和类型为 HostName 的名称,这是一个最多 216-1 字节的非空列表,编码主机名,如 https://www.rfc-editor.org/rfc/rfc6066#section-3 中所述。

关于ssl - 解释由 RFC6066 服务器名称指示定义的 SSL ClientHello SNI 消息扩展语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57315940/

上一篇:ssl - RSA 中的消息长度限制

下一篇:ssl - 使用 CA.key :openssl 签署用户证书

相关文章:

c# - 根据自签名证书颁发机构验证服务器证书

ssl - Zookeeper TLS 错误 : Unsuccessful handshake with session 0x0 (org. apache.zookeeper.server.NettyServerCnxnFactory)

ssl - Mosquitto - 内部和外部客户端配置

java - 如何在 Win10/Tomcat7/Java7 上从 Web 应用程序启用 TLSv1.1+ 出站通信?

c - RFC 1071 - 在 C 中计算 IP header 校验和混淆

php - Asana PHP API 连接错误 : 443

django - 在 Mac OSX 上启用 TLS 1.2

python - python中的SSL验证错误

http - 我可以用逗号分隔所有 HTTP header 吗?甚至授权?

HTTP 状态代码优先级和处理