6. Binary Packet Protocol (二进制数据包协议)
6. Binary Packet Protocol (二进制数据包协议)
每个数据包采用以下格式:
uint32 packet_length
byte padding_length
byte[n1] payload; n1 = packet_length - padding_length - 1
byte[n2] random padding; n2 = padding_length
byte[m] mac (Message Authentication Code - MAC); m = mac_length
各字段说明:
packet_length
数据包的长度 (字节数), 不包括 mac 或 packet_length 字段本身。
padding_length
random padding 的长度 (字节数)。
payload
数据包的有效内容。如果已协商压缩, 则此字段会被压缩。初始时, 压缩必须为 "none"。
random padding
任意长度的填充, 使得 (packet_length || padding_length || payload || random padding) 的总长度是密码块大小或 8 (取较大者) 的倍数。填充必须至少有 4 字节。填充应该由随机字节组成。填充的最大长度为 255 字节。
mac
消息认证码 (Message Authentication Code)。如果已协商消息认证, 则此字段包含 MAC 字节。初始时, MAC 算法必须为 "none"。
注意, packet_length, padding_length, payload 和 random padding 的连接长度必须是密码块大小或 8 (取较大者) 的倍数。即使使用流密码, 也必须强制执行此约束。还要注意, packet_length 字段也会被加密, 在发送或接收数据包时处理它需要特别小心。另外请注意, 插入可变数量的 random padding 可能有助于阻止流量分析。
数据包的最小大小为 16 字节 (或密码块大小, 取较大者) (加上 mac)。实现应该在接收到数据包的前 8 字节 (或密码块大小, 取较大者) 之后解密长度字段。
6.1. Maximum Packet Length (最大数据包长度)
所有实现必须能够处理未压缩有效载荷长度不超过 32768 字节且总数据包大小不超过 35000 字节的数据包 (包括 packet_length, padding_length, payload, random padding 和 mac)。35000 字节的最大值是任意选择的, 大于上述未压缩长度。实现应该支持更长的数据包 (在需要时)。例如, 如果实现想要发送大量证书, 如果标识字符串表明对方能够处理较大的数据包, 则可以发送较大的数据包。但是, 实现应该检查数据包长度是否合理, 以避免拒绝服务和/或缓冲区溢出攻击。
6.2. Compression (压缩)
如果已协商压缩, 则 payload 字段 (且仅此字段) 将使用协商的算法进行压缩。packet_length 字段和 mac 将从压缩后的有效载荷计算。加密将在压缩之后进行。
压缩可以是有状态的, 具体取决于方法。压缩必须对每个方向独立, 并且实现必须允许对每个方向独立选择算法。然而在实践中, 建议两个方向使用相同的压缩方法。
当前定义了以下压缩方法:
none REQUIRED 无压缩
zlib OPTIONAL ZLIB (LZ77) 压缩
"zlib" 压缩在 [RFC1950] 和 [RFC1951] 中描述。压缩上下文在每次密钥交换后初始化, 并从一个数据包传递到下一个数据包, 每个数据包结束时仅执行部分刷新。部分刷新意味着当前压缩块结束, 所有数据都将输出。如果当前块不是存储块, 则在当前块之后添加一个或多个空块, 以确保从当前块的块结束代码开始到数据包有效载荷结束至少有 8 位。
可以按照 [SSH-ARCH] 和 [SSH-NUMBERS] 中的规定定义其他方法。
6.3. Encryption (加密)
加密算法和密钥将在密钥交换期间协商。当加密生效时, 每个数据包的数据包长度、填充长度、有效载荷和填充字段必须使用给定的算法进行加密。
单个方向发送的所有数据包中的加密数据应该被视为单个数据流。例如, 初始化向量应该从一个数据包的末尾传递到下一个数据包的开头。所有密码应该使用有效密钥长度为 128 位或更长的密钥。
每个方向的密码必须独立运行。如果本地策略允许多个算法, 实现必须允许为每个方向独立选择算法。然而在实践中, 建议两个方向使用相同的算法。
当前定义了以下密码:
3des-cbc REQUIRED CBC 模式的三密钥 3DES
blowfish-cbc OPTIONAL CBC 模式的 Blowfish
twofish256-cbc OPTIONAL CBC 模式的 Twofish,
使用 256 位密钥
twofish-cbc OPTIONAL "twofish256-cbc" 的别名
(出于历史原因保留)
twofish192-cbc OPTIONAL 192 位密钥的 Twofish
twofish128-cbc OPTIONAL 128 位密钥的 Twofish
aes256-cbc OPTIONAL CBC 模式的 AES,
使用 256 位密钥
aes192-cbc OPTIONAL 192 位密钥的 AES
aes128-cbc RECOMMENDED 128 位密钥的 AES
serpent256-cbc OPTIONAL CBC 模式的 Serpent,
使用 256 位密钥
serpent192-cbc OPTIONAL 192 位密钥的 Serpent
serpent128-cbc OPTIONAL 128 位密钥的 Serpent
arcfour OPTIONAL 128 位密钥的 ARCFOUR 流密码
idea-cbc OPTIONAL CBC 模式的 IDEA
cast128-cbc OPTIONAL CBC 模式的 CAST-128
none OPTIONAL 无加密; 不推荐
"3des-cbc" 密码是三密钥三重 DES (加密-解密-加密), 其中密钥的前 8 字节用于第一次加密, 接下来的 8 字节用于解密, 后面的 8 字节用于最后的加密。这需要 24 字节的密钥数据 (其中实际使用 168 位)。要实现 CBC 模式, 必须使用外部链接 (即只有一个初始化向量)。这是一个 8 字节块的块密码。该算法在 [FIPS-46-3] 中定义。请注意, 由于该算法的有效密钥长度仅为 112 位 ([SCHNEIER]), 因此不符合 SSH 加密算法应使用 128 位或更长密钥的规范。然而, 出于历史原因, 该算法仍然是必需的; 基本上, 在撰写本文时所有已知的实现都支持该算法, 并且它通常被使用, 因为它是基本的可互操作算法。预计在未来某个时候, 另一种具有更好强度的算法将变得如此普遍和无处不在, 以至于 "3des-cbc" 的使用将通过另一个标准行动被弃用。
"blowfish-cbc" 密码是 CBC 模式的 Blowfish, 使用 128 位密钥 [SCHNEIER]。这是一个 8 字节块的块密码。
"twofish-cbc" 或 "twofish256-cbc" 密码是 CBC 模式的 Twofish, 使用 [TWOFISH] 中描述的 256 位密钥。这是一个 16 字节块的块密码。
"twofish192-cbc" 密码与上述相同, 但使用 192 位密钥。
"twofish128-cbc" 密码与上述相同, 但使用 128 位密钥。
"aes256-cbc" 密码是 CBC 模式的 AES (高级加密标准) [FIPS-197]。此版本使用 256 位密钥。
"aes192-cbc" 密码与上述相同, 但使用 192 位密钥。
"aes128-cbc" 密码与上述相同, 但使用 128 位密钥。
"serpent256-cbc" 密码是 CBC 模式的 Serpent, 使用 Serpent AES 提交中描述的 256 位密钥。
"serpent192-cbc" 密码与上述相同, 但使用 192 位密钥。
"serpent128-cbc" 密码与上述相同, 但使用 128 位密钥。
"arcfour" 密码是使用 128 位密钥的 Arcfour 流密码。Arcfour 密码被认为与 RC4 密码兼容 [SCHNEIER]。Arcfour (和 RC4) 存在弱密钥问题, 应谨慎使用。
"idea-cbc" 密码是 CBC 模式的 IDEA 密码 [SCHNEIER]。
"cast128-cbc" 密码是 CBC 模式的 CAST-128 密码, 使用 128 位密钥 [RFC2144]。
"none" 算法指定不进行加密。请注意, 此方法不提供机密性保护, 不推荐使用。如果选择此密码, 出于安全原因某些功能 (例如密码认证) 可能会被禁用。
可以按照 [SSH-ARCH] 和 [SSH-NUMBERS] 中的规定定义其他方法。
6.4. Data Integrity (数据完整性)
数据完整性通过在每个数据包中包含一个 MAC 来保护, 该 MAC 从共享密钥、数据包序列号和数据包内容计算得出。
消息认证算法和密钥在密钥交换期间协商。初始时, 不会有 MAC 生效, 其长度必须为零。密钥交换后, 所选 MAC 算法的 mac 将在加密之前从数据包数据的连接计算:
mac = MAC(key, sequence_number || unencrypted_packet)
其中 unencrypted_packet 是不包含 mac 的整个数据包 (长度字段、payload 和 random padding), sequence_number 是一个隐式的数据包序列号, 表示为 uint32。sequence_number 对于第一个数据包初始化为零, 并在每个数据包之后递增 (无论是否使用加密或 MAC)。它永远不会重置, 即使稍后重新协商密钥/算法也是如此。每 2^32 个数据包后它会回绕到零。数据包 sequence_number 本身不包含在通过网络发送的数据包中。
每个方向的 MAC 算法必须独立运行, 并且实现必须允许为两个方向独立选择算法。然而在实践中, 建议两个方向使用相同的算法。
从 MAC 算法得到的 mac 值必须在不加密的情况下作为数据包的最后部分传输。mac 的字节数取决于所选择的算法。
当前定义了以下 MAC 算法:
hmac-sha1 REQUIRED HMAC-SHA1 (摘要长度 = 密钥
长度 = 20)
hmac-sha1-96 RECOMMENDED HMAC-SHA1 的前 96 位 (摘要
长度 = 12, 密钥长度 = 20)
hmac-md5 OPTIONAL HMAC-MD5 (摘要长度 = 密钥
长度 = 16)
hmac-md5-96 OPTIONAL HMAC-MD5 的前 96 位 (摘要
长度 = 12, 密钥长度 = 16)
none OPTIONAL 无 MAC; 不推荐
"hmac-" 算法在 [RFC2104] 中描述。"-n" MAC 仅使用结果值的前 n 位。
SHA-1 在 [FIPS-180-2] 中描述, MD5 在 [RFC1321] 中描述。
可以按照 [SSH-ARCH] 和 [SSH-NUMBERS] 中的规定定义其他方法。
6.5. Key Exchange Methods (密钥交换方法)
密钥交换方法指定如何为加密和认证生成一次性会话密钥, 以及如何进行服务器认证。
已定义两种必需的密钥交换方法:
diffie-hellman-group1-sha1 REQUIRED
diffie-hellman-group14-sha1 REQUIRED
这些方法在第 8 节中描述。
可以按照 [SSH-NUMBERS] 中的规定定义其他方法。名称 "diffie-hellman-group1-sha1" 用于使用 [RFC2409] 中定义的 Oakley 组的密钥交换方法。SSH 维护自己的组标识符空间, 在逻辑上与 Oakley [RFC2412] 和 IKE 不同; 但是, 对于另一个组, 工作组采用了 [RFC3526] 分配的编号, 使用 diffie-hellman-group14-sha1 作为第二个定义组的名称。实现应将这些名称视为不透明标识符, 不应假设 SSH 使用的组与为 IKE 定义的组之间存在任何关系。
6.6. Public Key Algorithms (公钥算法)
此协议被设计为可以与几乎任何公钥格式、编码和算法 (签名和/或加密) 一起使用。
定义公钥类型有几个方面:
-
密钥格式: 密钥如何编码以及如何表示证书。此协议中的密钥 blob 除了密钥之外还可以包含证书。
-
签名和/或加密算法。某些密钥类型可能不支持签名和加密两者。密钥使用也可能受策略声明 (例如在证书中) 的限制。在这种情况下, 应该为不同的策略替代方案定义不同的密钥类型。
-
签名和/或加密数据的编码。这包括但不限于填充、字节顺序和数据格式。
当前定义了以下公钥和/或证书格式:
ssh-dss REQUIRED sign 原始 DSS 密钥
ssh-rsa RECOMMENDED sign 原始 RSA 密钥
pgp-sign-rsa OPTIONAL sign OpenPGP 证书 (RSA 密钥)
pgp-sign-dss OPTIONAL sign OpenPGP 证书 (DSS 密钥)
可以按照 [SSH-ARCH] 和 [SSH-NUMBERS] 中的规定定义其他密钥类型。
密钥类型必须始终明确已知 (通过算法协商或其他来源)。它通常不包含在密钥 blob 中。
证书和公钥编码如下:
string 证书或公钥格式标识符
byte[n] 密钥/证书数据
证书部分可以是零长度字符串, 但需要公钥。这是将用于认证的公钥。证书 blob 中包含的证书序列可用于提供授权。
未明确指定签名格式标识符的公钥/证书格式必须使用公钥/证书格式标识符作为签名标识符。
签名编码如下:
string 签名格式标识符 (由公钥/证书格式指定)
byte[n] 格式特定编码的签名 blob
"ssh-dss" 密钥格式具有以下特定编码:
string "ssh-dss"
mpint p
mpint q
mpint g
mpint y
这里, p, q, g 和 y 参数构成签名密钥 blob。
使用此密钥格式进行签名和验证根据数字签名标准 [FIPS-186-2] 使用 SHA-1 散列 [FIPS-180-2] 进行。
结果签名编码如下:
string "ssh-dss"
string dss_signature_blob
dss_signature_blob 的值编码为包含 r 后跟 s 的字符串 (它们是 160 位整数, 无长度或填充, 无符号, 采用网络字节顺序)。
"ssh-rsa" 密钥格式具有以下特定编码:
string "ssh-rsa"
mpint e
mpint n
这里 e 和 n 参数构成签名密钥 blob。
使用此密钥格式进行签名和验证根据 [RFC3447] 中的 RSASSA-PKCS1-v1_5 方案使用 SHA-1 散列执行。
结果签名编码如下:
string "ssh-rsa"
string rsa_signature_blob
rsa_signature_blob 的值编码为包含 s 的字符串 (它是一个整数, 无长度或填充, 无符号, 采用网络字节顺序)。
"pgp-sign-rsa" 方法表示证书、公钥和签名采用 OpenPGP 兼容的二进制格式 ([RFC2440])。此方法表示密钥是 RSA 密钥。
"pgp-sign-dss" 与上述相同, 但表示密钥是 DSS 密钥。