SSH 2.0 详尽的标准实现

很久之前(在和前任 Linux 认识之前)就听说了 SSH 一词,和旧爱在一起的时光一直舍不得用(Linux:连 VPS 都没有的渣渣还妄想用 SSH ?),就偶尔看了看 SSH 原理的一些资料。

直到最近在新欢上为了达到不可告人的目标需要配置 SSH ,在过程中发现一些和之前网上看的不一致的内容,同时那些资料各种不注重我想要的细节,于是怒刷 RFC 把协议规范给扫了一遍~

写这篇文章的目的是尝试通过极尽细节的描述,使读者对 SSH(2.0, 下同) 原理有清晰的认识,并纠正某些网络上的错误言论。
(因本文涉及大量细节部分,计划闲时逐步补充,没有最终完成稿一说,有需要补充的随时加上。)

正文

SSH 是一个密码协议,涉及大量密码学概念、知识。
您可能需要先简单了解密码学。 密码学科普
SSH 简介
RFC 4251: The Secure Shell (SSH) Protocol is a protocol for secure remote login and other secure network services over an insecure network.
SSH 是一个在不安全网络上为远程登录会话提供安全保障的协议。能有效防止信息泄露。
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: SSH-protoversion-softwareversion SP comments CR LF
众所周知,SSH 有两个主版本(1.x 与 2.0)。通常,服务器先发送自己的标识串。客户端根据服务器的支持情况在自己标识串中选择自己要使用的协议版本。
如果服务器同时支持 1.x 和 2.0,协议版本为 1.99
如果客户端尝试调戏服务器(发送了服务器不支持的协议版本),那么服务器有权立即断开连接。
TIPS: 2.0 版本允许任何一方发送完标识串后立即进行密钥协商(发送相关报文),服务器在同时支持两个主版本的情况下不会使用这个特性,然而如果一个 2.0 版本的客户端这样对待一个 1.x 版本的服务器,当它意识到服务器的“古老”时,应该断开连接从头重来。

  密钥与算法协商
    支持的算法
在讲述协商之前,先列举 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
TIPS: 尽管强制要求 SSH 软件实现带 [R] 的算法,但是实际使用的用户可能禁用不想使用的算法,因此这并不意味着一定能成功完成算法协商。

    算法协商
按理说密钥交换应该滞后于算法协商(因为密钥交换所用的算法需要经过协商),而在 SSH 中两个步骤是同时启动的,算法协商的第一个报文就将用于密钥交换。

首先双方向对方发送密钥交换初始报文,其中包括自己支持的算法列表——密钥交换算法,服务器主机密钥算法,加密算法(客户端到服务器),加密算法(服务器到客户端),消息认证码算法(客户端到服务器),消息认证码算法(服务器到客户端),压缩算法(客户端到服务器),压缩算法(服务器到客户端)。
不同类型的算法使用不同的协商方案。

密钥交换算法:双方都选择列表中的第一个算法,如果相同,则必须使用该算法;否则,按序枚举客户端(支持的,下同)算法列表,使用找到的第一个满足以下条件的算法作为密钥交换算法。否则连接终止。

  1. 服务器支持该算法。
  2. 如果这个算法需要一个能用于加密的服务器主机密钥(Host key),那么双方都必须同时支持至少一种能用于加密的服务器主机密钥算法。
  3. 如果这个算法需要一个能用于数字签名的服务器主机密钥(Host key),那么双方都必须同时支持至少一种能用于数字签名的服务器主机密钥算法。

 
服务器主机密钥算法:如上所说,该算法的选取依赖密钥交换算法的选取。在经过上述筛选后,若有多个算法可被使用,选取客户端算法列表中位置靠前的算法。

加密算法、消息认证码算法、压缩算法(对任意方向的算法都使用相同的规则):枚举客户端算法列表,选取第一个服务器也支持的算法。

    密钥交换(DH)

您可能需要先大致了解 DH 密钥交换算法。 迪菲-赫尔曼密钥交换 – 维基百科
定义大素数 p ,GF(p) 的一个子群上的生成元 g ,该子群的阶 q 。
定义 V_S 为服务器的标识串(用于版本协商的),V_C 为客户端的标识串,K_S 为服务器主机密钥,I_C、I_S 分别是客户端和服务器发送的 密钥交换初始报文

  1. 首先客户端随机选取一个整数 x 满足(1 < x < q),计算 e = g^x mod p 。把 e 发给服务器。
  2. 服务器也随机选取一个整数 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 发送给客户端。
  3. 客户端收到服务器的 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 连接用的密钥已完全确立。自此以后的所有通信都将经过加密

TIPS: 使用该算法做密钥交换时需要对哈希值 H 签名,因此协商密钥交换算法时,这个算法需要一个能够签名的服务器主机密钥,因此服务器主机密钥必须能够签名。(DSA, RSA, 包括 OpenSSH 新引入的 ECDSA 都是至少能用于数字签名的算法)
TIPS: 刚才说到有可能会重新进行密钥交换。这通常发生在一个 SSH 连接持续了足够长的时间或传输了足够多的数据,SSH 认为当前密钥安全性受到挑战,因此重新进行密钥交换。如上文所说,密钥交换输出的 H 会发生改变,但是 session_id 不再变化。

用户认证协议
还是先列举下 SSH 标准定义的认证方式:公钥认证(publickey)[R],密码认证(password),基于主机的认证(hostbased),不认证[不推荐]

TIPS: SSH 2.0 允许使用多种认证,每一种认证中又可以给出多个认证方式。客户端完成其中任意一种中的全部认证方式才能完成认证。
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),用该公钥的私钥签名,发送给服务器。

服务器验证签名,成功则认证成功,否则失败。

TIPS: 如上所述,公钥认证所使用的公钥算法一定要能够用于数字签名!

  密码认证
客户端把用户名和密码一股脑发过去,服务器检查下就是了~~

  主机认证
服务器 用和客户端验证服务器主机密钥差不多的方法 验证客户端,完了~

OK! SSH 完成了认证,该连接已经是加密并授权的了。接下来是连接管理……(喂你好像没说要讲这个吧啊!)(嗯是么?那就跳过吧……)(………………)

连接协议
(等我找到女朋友了一定会来补的\(“▔□▔)/)

本文卒。



  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值