解释由 RFC6066 服务器名称指示定义的 SSL ClientHello SNI 消息扩展语法
Explaining SSL ClientHello SNI message extension syntax defined by RFC6066 Server Name Indication
RFC6066 定义类型 server_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()));
}
字节reader:
private static int getInt16(ByteBuffer input) {
return ((input.get() & 0xFF) << 8) | (input.get() & 0xFF);
}
Here 是如何读取数据的好例子。例如,扩展类型是通过读取 2 个字节来定义的 - 所以另一个问题是 - 哪个 RFC 定义了它?
如果您已经有了实现它的源代码,还需要了解什么?
用于抽象模式的格式源自XDR but is defined specifically in each TLS specification, like for the last one in 3. Presentation Language
因此,如果我们逐条进行:
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
的值为
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 值适合一个字节,此结构使用 space 的一个字节),如规范中所述:
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到2之间的可变数组16-1字节
- 此数组的每个元素都属于
ServerName
类型,如先前定义的那样
换句话说:
- 一个
ServerNameList
结构是一个元素列表,每个元素都是ServerName
类型
- 这个列表不能为空,因为它至少有 1 个字节的长度
(最多 216-1 个字节)
- a
ServerName
编码一个 name_type
(只能是“host_name”,也就是值 0)和一个类型为 HostName
的名称,它是最多 216-1 个字节的非空列表,编码主机名,如 https://www.rfc-editor.org/rfc/rfc6066#section-3 中所述
RFC6066 定义类型 server_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()));
}
字节reader:
private static int getInt16(ByteBuffer input) {
return ((input.get() & 0xFF) << 8) | (input.get() & 0xFF);
}
Here 是如何读取数据的好例子。例如,扩展类型是通过读取 2 个字节来定义的 - 所以另一个问题是 - 哪个 RFC 定义了它?
如果您已经有了实现它的源代码,还需要了解什么?
用于抽象模式的格式源自XDR but is defined specifically in each TLS specification, like for the last one in 3. Presentation Language
因此,如果我们逐条进行:
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
的值为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 值适合一个字节,此结构使用 space 的一个字节),如规范中所述:
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到2之间的可变数组16-1字节
- 此数组的每个元素都属于
ServerName
类型,如先前定义的那样
换句话说:
- 一个
ServerNameList
结构是一个元素列表,每个元素都是ServerName
类型
- 这个列表不能为空,因为它至少有 1 个字节的长度 (最多 216-1 个字节)
- a
ServerName
编码一个name_type
(只能是“host_name”,也就是值 0)和一个类型为HostName
的名称,它是最多 216-1 个字节的非空列表,编码主机名,如 https://www.rfc-editor.org/rfc/rfc6066#section-3 中所述