1. 算法协商
在密钥交换开始前,客户端和服务端会协商确定本次会话使用的算法组合。具体过程如下:
交换算法列表
- 客户端和服务端各自发送支持的算法列表,包括:
- 密钥交换算法(如
diffie-hellman-group14-sha256
) - 加密算法(如
aes256-ctr
) - MAC算法(如
hmac-sha2-256
) - 压缩算法(如
none
表示不压缩)
- 密钥交换算法(如
- 客户端和服务端各自发送支持的算法列表,包括:
选择共同支持的算法
- 双方从对方的列表中按优先级选择第一个匹配的算法。
- 例如:
- 客户端发送的密钥交换算法列表:
curve25519-sha256, diffie-hellman-group14-sha256
- 服务端支持的列表:
diffie-hellman-group14-sha256, ecdh-sha2-nistp256
- 最终选定
diffie-hellman-group14-sha256
。
- 客户端发送的密钥交换算法列表:
2. 密钥交换流程
以 Diffie-Hellman Group 14 (2048-bit) 为例,说明临时密钥的生成与会话密钥的计算。
步骤 1:生成临时密钥对
- 客户端和服务端各自独立生成一对 临时 公私钥:
- 私钥:随机大整数 ( x )(客户端私钥为 ( x_C ),服务端私钥为 ( x_S )),保密存储。
- 公钥:通过公式 ( g^{x} \mod p ) 计算得到(( g ) 和 ( p ) 是公开的 Group14 参数)。
步骤 2:交换公钥
- 客户端将临时公钥 ( g^{x_C} \mod p ) 发送给服务端。
- 服务端将临时公钥 ( g^{x_S} \mod p ) 发送给客户端。
步骤 3:计算共享密钥(Shared Secret)
- 客户端使用服务端的公钥计算共享密钥:
[
K = (g{x_S}){x_C} \mod p = g^{x_S x_C} \mod p
] - 服务端使用客户端的公钥计算共享密钥:
[
K = (g{x_C}){x_S} \mod p = g^{x_C x_S} \mod p
] - 根据模幂运算性质,双方最终得到相同的共享密钥 ( K )。
步骤 4:派生会话密钥
共享密钥 ( K ) 不会直接用于加密数据,而是作为输入,结合其他参数通过哈希函数生成最终会话密钥。
具体流程:
- 收集交换参数:
- 客户端和服务端的临时公钥(( g^{x_C}, g^{x_S} ))
- 共享密钥 ( K )
- 双方初始交换的随机数(Client/Server Hello 中的 nonce)
- 哈希计算:
使用协商的哈希算法(如 SHA-256)处理所有参数:
[
\text{会话密钥} = \text{SHA-256}(K || g^{x_C} || g^{x_S} || \text{ClientNonce} || \text{ServerNonce})
] - 分割密钥材料:
哈希结果被分割为多个密钥,用于不同用途:- 客户端到服务端的加密密钥
- 服务端到客户端的加密密钥
- 客户端到服务端的 MAC 密钥
- 服务端到客户端的 MAC 密钥
3. 服务端公钥 vs 临时公钥
服务端长期公钥(主机密钥):
- 用途:验证服务端身份(防止中间人攻击)。
- 存储位置:服务端的
/etc/ssh/ssh_host_rsa_key
(默认路径)。 - 客户端首次连接时需手动确认其指纹,之后存储在
~/.ssh/known_hosts
。
临时公钥:
- 用途:仅用于本次会话的密钥交换(Diffie-Hellman)。
- 生命周期:会话结束后立即销毁,确保前向保密(即使长期密钥泄露,历史会话仍安全)。
4. 完整流程示例
假设客户端(C)和服务端(S)使用 Diffie-Hellman Group14:
参数定义
- 公共素数 ( p = 2^{2048} - 2^{1984} - 1 + 2^{64} \times \lfloor 2^{1918} \pi \rfloor + 124476 )
- 生成器 ( g = 2 )
密钥生成
- C 随机选择私钥 ( x_C = 12345 ),计算公钥 ( g^{x_C} \mod p = A )
- S 随机选择私钥 ( x_S = 67890 ),计算公钥 ( g^{x_S} \mod p = B )
交换公钥
- C → S 发送 ( A )
- S → C 发送 ( B )
计算共享密钥
- C 计算 ( K = B^{x_C} \mod p = (g{x_S}){x_C} \mod p = g^{x_S x_C} \mod p )
- S 计算 ( K = A^{x_S} \mod p = (g{x_C}){x_S} \mod p = g^{x_C x_S} \mod p )
- 结果相同:( K = g^{x_C x_S} \mod p )
派生会话密钥
- 输入 ( K )、( A )、( B )、ClientNonce、ServerNonce 到 SHA-256
- 输出 256 位哈希值,分割为多个密钥。
5. 安全性保障
- 前向保密(Perfect Forward Secrecy, PFS):
每次会话使用临时密钥,即使攻击者获取服务端长期私钥,也无法解密历史会话。 - 抗中间人攻击:
客户端通过验证服务端长期公钥指纹确认身份。 - 算法强度:
2048-bit 的 Group14 提供足够安全性(截至 2023 年未被攻破)。
总结
SSHv2 的密钥交换通过 临时密钥对 和 Diffie-Hellman 算法 确保会话密钥的安全生成,同时通过 哈希函数派生 和 前向保密设计 抵御多种攻击。服务端长期公钥用于身份验证,而临时公钥仅服务于本次会话,两者分工明确,共同保障协议的安全性。