SSH 2.0 详尽的标准实现
很久之前(在和前任 Linux 认识之前)就听说了 SSH 一词,和旧爱在一起的时光一直舍不得用(Linux:连 VPS 都没有的渣渣还妄想用 SSH ?),就偶尔看了看 SSH 原理的一些资料。直到最近在新欢上为了达到不可告人的目标需要配置 SSH ,在过程中发现一些和之前网上看的不一致的内容,同时那些资料各种不注重我想要的细节,于是怒刷 RFC 把协议规范给扫了一遍~写这篇文章
很久之前(在和前任 Linux 认识之前)就听说了 SSH 一词,和旧爱在一起的时光一直舍不得用(Linux:连 VPS 都没有的渣渣还妄想用 SSH ?),就偶尔看了看 SSH 原理的一些资料。
直到最近在新欢上为了达到不可告人的目标需要配置 SSH ,在过程中发现一些和之前网上看的不一致的内容,同时那些资料各种不注重我想要的细节,于是怒刷 RFC 把协议规范给扫了一遍~
写这篇文章的目的是尝试通过极尽细节的描述,使读者对 SSH(2.0, 下同) 原理有清晰的认识,并纠正某些网络上的错误言论。
(因本文涉及大量细节部分,计划闲时逐步补充,没有最终完成稿一说,有需要补充的随时加上。)
正文
您可能需要先简单了解密码学。 密码学科普
SSH 协议框架主要分为三个协议:SSH 传输层协议[RFC 4253]、SSH 用户认证协议[RFC 4252]、SSH 连接协议[RFC 4254]。本文内容暂只包括 传输层协议 和 用户认证协议的密匙认证部分 。
传输层协议
首先通过 TCP/IP 协议栈连接到目标主机。
版本协商
连接建立后,客户端和服务器都必须发送自己的标识串给对方。标识串的格式为(\r 是 CR 回车符(ASCII 0x0D),\n 是 LF 换行符(ASCII 0x0A)):
1
2
|
SSH-<协议版本>-<软件版本>[ 注释]\r\n
示例:SSH-2.0-OpenSSH_6.3_hpn13v11 FreeBSD-20130918\r\n
|
密钥与算法协商
支持的算法
在讲述协商之前,先列举 RFC 4253 中声明的算法。([R] 表示 SSH 软件必须实现的算法,[RE] 表示标准推荐实现的算法)
压缩算法 | none[R], zlib |
加密算法 | 3des-cbc[R], blowfish-cbc, twofish256-cbc, twofish-cbc, twofish192-cbc, twofish128-cbc, aes256-cbc, aes192-cbc, aes128-cbc[RE], serpent256-cbc, serpent192-cbc, serpent128-cbc, arcfour, idea-cbc, cast128-cbc, none[不推荐] |
数据完整性算法 | hmac-sha1[R], hmac-sha1-96[RE], hmac-md5, hmac-md5-96, none[不推荐] |
密钥交换算法 | diffie-hellman-group1-sha1[R], diffie-hellman-group14-sha1[R] |
公钥算法 | ssh-dss[R], ssh-rsa[RE], pgp-sign-rsa, pgp-sign-dss |
算法协商
按理说密钥交换应该滞后于算法协商(因为密钥交换所用的算法需要经过协商),而在 SSH 中两个步骤是同时启动的,算法协商的第一个报文就将用于密钥交换。
首先双方向对方发送密钥交换初始报文,其中包括自己支持的算法列表——密钥交换算法,服务器主机密钥算法,加密算法(客户端到服务器),加密算法(服务器到客户端),消息认证码算法(客户端到服务器),消息认证码算法(服务器到客户端),压缩算法(客户端到服务器),压缩算法(服务器到客户端)。
不同类型的算法使用不同的协商方案。
密钥交换算法:双方都选择列表中的第一个算法,如果相同,则必须使用该算法;否则,按序枚举客户端(支持的,下同)算法列表,使用找到的第一个满足以下条件的算法作为密钥交换算法。否则连接终止。
- 服务器支持该算法。
- 如果这个算法需要一个能用于加密的服务器主机密钥(Host key),那么双方都必须同时支持至少一种能用于加密的服务器主机密钥算法。
- 如果这个算法需要一个能用于数字签名的服务器主机密钥(Host key),那么双方都必须同时支持至少一种能用于数字签名的服务器主机密钥算法。
服务器主机密钥算法:如上所说,该算法的选取依赖密钥交换算法的选取。在经过上述筛选后,若有多个算法可被使用,选取客户端算法列表中位置靠前的算法。
加密算法、消息认证码算法、压缩算法(对任意方向的算法都使用相同的规则):枚举客户端算法列表,选取第一个服务器也支持的算法。
密钥交换(DH)
定义 V_S 为服务器的标识串(用于版本协商的),V_C 为客户端的标识串,K_S 为服务器主机密钥,I_C、I_S 分别是客户端和服务器发送的 密钥交换初始报文 。
- 首先客户端随机选取一个整数 x 满足(1 < x < q),计算 e = g^x mod p 。把 e 发给服务器。
- 服务器也随机选取一个整数 y (0 < y < q),计算 f = g^y mod p 。然后获得客户端的 e ,计算 K = e^y mod p 。计算哈希值 H ,参与哈希运算的内容有 V_C, V_S, I_C, I_S, K_S, e, f, K 。然后用服务器主机密钥的私钥对这个哈希值做数字签名,签名值 s 。把 K_S, f, s 发送给客户端。
- 客户端收到服务器的 K_S, f, s 后,先验证 K_S 的有效性(还记得第一次 SSH 远端主机时,程序打印了一串服务器主机密钥指纹,询问你是否信任该主机吧?)(通常比对 known_hosts 文件),然后计算 K = f^x mod p (附注:DH 密钥交换算法里有数学证明服务器和客户端产生的 K 是一致的,这个 K 即被称作共享信息,但是该值并未在网络上传输过),然后计算哈希值 H ,参与哈希运算的内容和第二步服务器做的相同,最后校验服务器的签名 s 。
DH 算法完成后,产生了一个双方共享的信息 K (再次重复该值第三方是不知道的),同时双方都有一个哈希值 H 。这个 H 即为本次会话的标识符(session_id),即使因某原因重新进行了密钥交换,该标识符也不会修改!
然后产生密钥:
加密密钥(客户端到服务器):使用和计算 H 相同的哈希算法计算一个哈希值 K1(参与哈希的内容包括 K, H,“C”(ASCII 67), session_id)(附注:H 和 session_id 不一定相同哦~)。从中截取等同于所用加密算法密钥长度的字节作为加密密钥。如果哈希值长度不够所需密钥长度,则在哈希值后面填充一个哈希值(参与内容包括 K, H, K1),如果还不够,填充哈希值(参与内容包括 K, H, K1, K2),如果还不够,……
加密密钥(服务器到客户端):同上述法则,区别在参与哈希值 K1 运算的内容中使用字符 “D” 替换上述的 “C”。
HMAC 用密钥(客户端到服务器):同上,使用字符 “E” 。
HMAC 用密钥(服务器到客户端):同上,使用字符 “F” 。
至此,本次 SSH 连接用的密钥已完全确立。自此以后的所有通信都将经过加密。
用户认证协议
还是先列举下 SSH 标准定义的认证方式:公钥认证(publickey)[R],密码认证(password),基于主机的认证(hostbased),不认证[不推荐]
1
2
3
4
5
|
举例:在 /etc/ssh/sshd_config 中设置(该设置项在 OpenSSH 6.2 中引入)
AuthenticationMethods publickey,password password,hostbased
意味着客户端有两种选择:
1)先完成公钥认证,再完成密码认证
2)先完成密码认证,再完成主机认证
|
公钥认证
SSH 标准定义的公钥算法在算法协商的地方说过了。
该方式要求客户端拥有一对公钥密码,且将公钥置放于服务器端。
客户端发起公钥认证请求,告知服务器自己的公钥算法名和公钥。若服务器表示不会这种算法或者找不到这个公钥,果断拒绝认证。(秀优越和搭讪什么的都去死吧!)
服务器回复允许公钥认证。客户端对自己刚发的公钥认证请求(其中包括 session_id)稍作简单修改(其实就是把一个 boolean 变量从 FALSE 改成 TRUE),用该公钥的私钥签名,发送给服务器。
服务器验证签名,成功则认证成功,否则失败。
密码认证
客户端把用户名和密码一股脑发过去,服务器检查下就是了~~
主机认证
服务器 用和客户端验证服务器主机密钥差不多的方法 验证客户端,完了~
OK! SSH 完成了认证,该连接已经是加密并授权的了。接下来是连接管理……(喂你好像没说要讲这个吧啊!)(嗯是么?那就跳过吧……)(………………)
连接协议
(等我找到女朋友了一定会来补的\(“▔□▔)/)
本文卒。
- 本文固定链接: http://www.f2light.com/blog/ssh-detail-spec/
- 转载请注明: 小虾米 2013年12月03日 于 珍珑明境 发表
更多推荐
所有评论(0)