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 密钥.