SSH 的握手比 TLS 1.3 啰嗦得多:明文的版本串、双方各列一个算法清单的 KEXINIT、密钥交换、切换密钥、再认证。这篇在本机起一个 paramiko 服务器,读真实公共 SSH 服务的 KEXINIT(只读不认证),看算法的演进,验证主机密钥首次信任的机制,并量出握手需要几个往返。
本机 paramiko 服务器:版本串 'SSH-2.0-paramiko_5.0.0'(格式 SSH-协议版本-软件版本 [注释];协议版本 2.0) github.com:版本串 'SSH-2.0-1f08b963'(格式 SSH-协议版本-软件版本 [注释];协议版本 2.0) gitlab.com:版本串 'SSH-2.0-GitLab-SSHD'(格式 SSH-协议版本-软件版本 [注释];协议版本 2.0)
明文的版本串,之后双方各发一个包含 10 个名字列表的 KEXINIT。
本机 paramiko 服务器:消息类型 20(20 = SSH_MSG_KEXINIT),cookie 4853d5f694df8a71…(16 字节随机数)
kex_algorithms 8 个:curve25519-sha256@libssh.org, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group16-sha512 …
server_host_key_algorithms 1 个:ecdsa-sha2-nistp256
encryption c→s 9 个:aes128-ctr, aes192-ctr, aes256-ctr, aes128-cbc, aes192-cbc …
encryption s→c 9 个:aes128-ctr, aes192-ctr, aes256-ctr, aes128-cbc, aes192-cbc …
mac c→s 8 个:hmac-sha2-256, hmac-sha2-512, hmac-sha2-256-etm@openssh.com, hmac-sha2-512-etm@openssh.com, hmac-sha1 …
mac s→c 8 个:hmac-sha2-256, hmac-sha2-512, hmac-sha2-256-etm@openssh.com, hmac-sha2-512-etm@openssh.com, hmac-sha1 …
compression c→s 1 个:none
compression s→c 1 个:none
github.com:消息类型 20(20 = SSH_MSG_KEXINIT),cookie 34cff8247bae6780…(16 字节随机数)
kex_algorithms 10 个:mlkem768x25519-sha256, sntrup761x25519-sha512, sntrup761x25519-sha512@openssh.com, curve25519-sha256, curve25519-sha256@libssh.org …
server_host_key_algorithms 5 个:ssh-ed25519, ecdsa-sha2-nistp256, rsa-sha2-512, rsa-sha2-256, ssh-rsa
encryption c→s 6 个:chacha20-poly1305@openssh.com, aes256-gcm@openssh.com, aes128-gcm@openssh.com, aes256-ctr, aes192-ctr …
encryption s→c 6 个:chacha20-poly1305@openssh.com, aes256-gcm@openssh.com, aes128-gcm@openssh.com, aes256-ctr, aes192-ctr …
mac c→s 4 个:hmac-sha2-512-etm@openssh.com, hmac-sha2-256-etm@openssh.com, hmac-sha2-512, hmac-sha2-256
mac s→c 4 个:hmac-sha2-512-etm@openssh.com, hmac-sha2-256-etm@openssh.com, hmac-sha2-512, hmac-sha2-256
compression c→s 2 个:none, zlib@openssh.com
compression s→c 2 个:none, zlib@openssh.com
gitlab.com:消息类型 20(20 = SSH_MSG_KEXINIT),cookie 3f108a789590ff98…(16 字节随机数)
kex_algorithms 9 个:mlkem768x25519-sha256, curve25519-sha256, curve25519-sha256@libssh.org, ecdh-sha2-nistp256, ecdh-sha2-nistp384 …
server_host_key_algorithms 6 个:ssh-dss, ecdsa-sha2-nistp256, ssh-ed25519, rsa-sha2-256, rsa-sha2-512 …
encryption c→s 6 个:aes128-gcm@openssh.com, aes256-gcm@openssh.com, chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr …
encryption s→c 6 个:aes128-gcm@openssh.com, aes256-gcm@openssh.com, chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr …
mac c→s 6 个:hmac-sha2-256-etm@openssh.com, hmac-sha2-512-etm@openssh.com, hmac-sha2-256, hmac-sha2-512, hmac-sha1 …
mac s→c 6 个:hmac-sha2-256-etm@openssh.com, hmac-sha2-512-etm@openssh.com, hmac-sha2-256, hmac-sha2-512, hmac-sha1 …
compression c→s 1 个:none
compression s→c 1 个:nonegithub 与 gitlab 都已经把后量子混合算法放在第一位;本机 paramiko 只有传统的 curve25519 / ecdh;gitlab 还保留了 ssh-dss 这样的老算法。
密钥交换完成用时 6.8 ms;服务器版本串 'SSH-2.0-paramiko_5.0.0';商定:加密 aes128-ctr,MAC hmac-sha2-256,压缩 none;kex 引擎 None
kex algos: curve25519-sha256@libssh.org, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group16-sha512, diffie-hellman-group-exchange-sha256, diffie-hellman-group14-sha256,
kex algos: curve25519-sha256@libssh.org, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group16-sha512, diffie-hellman-group-exchange-sha256, diffie-hellman-group14-sha256,
Switch to new keys ...
服务器主机密钥:类型 ecdsa-sha2-nistp256,指纹 SHA256:knjhudrbLItfSTRj6tCZB7oQ4AMaJe33mMdn9cM3apg(客户端要拿这个指纹去和已知的比较)aes128-ctr + hmac-sha2-256 + 无压缩;“Switch to new keys”之后通道加密。
auth_none 的回答:服务器接受的方式 ['publickey', 'password'] 错误的密码:失败 AuthenticationException 正确的密码:成功,仍需的方式 [];已认证 True 错误的密钥(SHA256:YykeCmuVny3m/auCS…):失败 AuthenticationException 正确的密钥(SHA256:8adI61bJX2J4TEO0k…):成功;已认证 True
在加密通道里进行:先问方式,密码或公钥。
第一次连接:记下指纹 SHA256:knjhudrbLItfSTRj6tCZB7o…(首次信任) 指纹与记录的一致,继续 换一台新的服务器(新的主机密钥)占用同一个“地址”后:⚠ 指纹变了!记录的 SHA256:knjhudrbLItfSTRj6…,现在是 SHA256:EOeuDpMcXaxXsea2j…——客户端应该拒绝(OpenSSH 会显示 REMOTE HOST IDENTIFICATION HAS CHANGED)
第一次记下指纹,之后指纹变了就是警报。
TCP 握手(ms) 版本交换 + 密钥交换(ms) 认证(ms) 合计(ms) (每行一次)
100 407 202 709
101 405 204 711
101 405 204 709
101 403 201 705
中位数:TCP 101 ms(1 个往返)、版本 + 密钥交换 405 ms(≈ 4.0 个往返)、认证 203 ms(≈ 2.0 个往返);合计 709 ms ≈ 7.1 个往返TCP 1 + 密钥交换约 4 + 认证约 2 个往返。