SSH overview

2022-04-02 15:10:15 浏览数 (1)

请注意:本文编写于 2021-10-22,其中某些信息可能已经失去时效性。

前言

SSH 是什么

Secure Shell(安全外壳协议,简称 SSH)是一种加密的网络传输协议,可以再不安全的网络中为网络服务提供安全的传输环境。 ——维基百科

通过维基百科的说明可以看出 SSH 实际上指的是一种加密的网络传输协议,而我们经常用来登录远程主机的 ssh 命令实际上是某个软件对 SSH 这种协议的包装实现,其中最常见的开源实现方案是 OpenSSH(OpenBSD Secure Shell)。

SSH 目前还是比较可靠的,利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。通过 SSH 可以对所有传输的数据进行加密,也能够防止 DNS 欺骗和 IP 欺骗。SSH 的另一项优点是其传输的数据可以是经过压缩的,所以可以加快传输速度。

SSH 当下有两个版本,分别是 SSHv1 和 SSHv2,v2 是主流版本,v1 版本存在中间人攻击的安全风险。

基本流程

SSH 协议规定的通讯流程可以分解成几个主要阶段:

  1. SSH 协议版本协商阶段
  2. 密钥和算法协商阶段
  3. 客户端认证阶段
  4. 会话请求阶段
  5. 交互会话阶段

SSH 协议版本协商阶段

  1. 服务端打开服务端口(默认为 22),等待客户端连接;
  2. 客户端向服务端发起 TCP 连接,双方完成握手并建立连接;
  3. 服务端通过 TCP 连接向客户端发送一个包含 SSH 版本信息的报文;
  4. 客户端收到报文后对比报文给出的版本信息和自身版本信息:
    1. 报文版本低于自身版本但自身能够兼容,使用报文版本进行通信;
    2. 其他情况下使用自身版本进行通信;
  5. 客户端将确定好的版本号通过 TCP 连接发送给服务端,服务端判断是否支持:
    1. 支持,进入密钥和算法协商阶段;
    2. 不支持,无法完成 SSH 连接,中断此次请求;

密钥和算法协商阶段

  1. 服务端和客户端通过 TCP 连接分别发送算法协商报文给对端,报文中包含自己支持的公钥算法列表,加密算法列表,MAC 算法列表,压缩算法列表等;
  2. 与协议版本协商阶段类似,服务端和客户端根据自己和对端支持的算法来决定最终使用的算法;
  3. 服务端和客户端利用 Diffie-Hellman 密钥交换使用算法,主机密钥对等参数,生成共享密钥和会话 ID;
  4. 至此加密的 SSH 通信通道建立完成,在后续的通信过程中共享密钥用于两端对传输数据的加密和解密,会话 ID 用于认证过程。
Diffie-Hellman 算法

DH 算法可以在一个不安全的信道上建立安全连接,从而解决不安全信道上信息安全交互的问题。

假设 A 与 B 要在不安全信道上使用 DH 算法安全地交换信息,大致流程如下:

  1. A 与 B 经过协商选定一个质数 p 及其本原根 g;
  2. A 生成随机数 a in [1, p-1],计算 Y_{A} = g^{a} mod p
  3. B 生成随机数 b in [1, p-1],计算 Y_{B} = g^{b} mod p
  4. 双方交换各自生成的 Y
  5. A 计算 K = Y_{B}^{a} mod p
  6. B 计算 K = Y_{A}^{b} mod p
  7. 经过上述过程,A 和 B 都得到了安全的共享密钥 K

注 1:流程中的数理知识这里不做普及,因为博主本身也不是很懂; 注 2:这里给出的仅仅是一个粗略的原理解释,并非最佳实现过程;

算法成立的原因:再此方法中公开数据有 p,g,Y_{A},Y_{B},若想要通过公开数据计算 K,则需要求取 Y_{A} = g^{a} mod p mid Y_{B} = g^{b} mod p 中的 a 或 b,求解此类问题一般使用穷举法,时间复杂度为 O(p),只要 p 足够大就能够保证此方法目前可以达到计算机安全的要求。

SSH 如何使用 Diffie-Hellman 算法

博主技术有限,没筛选出这一流程的 TCP 包,因此参考 第三篇参考文章 给出 diffie-hellman-group-exchange-sha256 的大致实现流程:

  1. 客户端通过 TCP 连接发送报文通知服务端开始 DH 交换流程;
  2. 服务端将选择好的 p 和 g 发送给客户端(含义参考上一小节);
  3. 客户端接收到 p 和 g 后生成自己的 Y-客户端,并返回给服务端;
  4. 服务端接收到 Y-客户端后:
    1. 通过计算得到密钥 K;
    2. 使用 sha256 算法将一些已知信息加密为 H-服务端并用 rsa 为其签名;
    3. 将 rsa 的公钥,Y-服务端,rsa 签名后的 H-服务端发送给客户端;
  5. 客户端接收到服务端的返回值:
    1. 计算出相同的密钥 K;
    2. 同样使用 sha256 算法将相同信息加密为 H-客户端;
    3. 利用 rsa 服务端公钥得到 H-客户端签名与 H-服务端签名进行对比;
    4. 校验无误,返回特定报文表示密钥交换完成,以后数据都由此密钥进行加密。

H 的计算方法:H = hash(VC parallel VS parallel IC parallel IS parallel KS parallel YC parallel YS parallel K)

类型

名称

意义

string

VC

客户端的初始报文

string

VS

服务端的初始报文

string

IC

客户端 SSH_MSG_KEX_INIY 的有效载荷

string

IS

服务端 SSH_MSG_KEX_INIT 的有效载荷

string

KS

服务端主机密钥(host key 一般是 RSA 公钥)

string

YC

Y-客户端

string

YS

Y-服务端

string

K

通过 DH 产生的共享密钥

以上内容按顺序进行拼接,不夹杂或尾随多余字符,拼接后的字符串进行 sha256 计算出结果就是 H 即 session_id。只有会话第一次密钥交换生成的 H 是 session_id,后面再进行密钥交换时,session_id 不会改变。

后续通信一般是采用 AES 算法进行加密,密钥计算方法:hash(K parallel H parallel W parallel session-id),其中 W 代指单个大写的 ASCII 字母,不同的加密秘钥使用不同的字符来计算。

字母

类型

A

客户端到服务端的初始 IV

B

服务端到客户端的初始 IV

C

客户端到服务端的加密秘钥

D

服务端到客户端的加密秘钥

E

客户端到服务端的完整性秘钥

F

服务端到客户端的完整性秘钥

经过计算得到字符串 RE,如果我们想要的秘钥长度比 RE 长,则在 RE 后面继续加上一个值:hash(K parallel H parallel RE) 成为一个加长的 RE。如果还不够,则继续使用同样方法进行累加。

注 1:关于秘钥计算公式中 H 和 session-id 同时出现博主表示存疑,但是没找到更多资料所以暂时先这么写了,如果具体实现和给出内容有出入的话希望您能不吝赐教,第一时间联系博主进行修改。

延伸:SSH 为什么要使用 Diffie-Hellman 算法

我对 SSH 协商过程的理解:

  1. A 利用 RSA 算法生成公钥 A 和私钥 A,B 同上生成公钥 B 和私钥 B;
  2. A 把公钥 A 发给 B;
  3. B 把公钥 B 发给 A;
  4. 后续通讯过程 A 用公钥 B 加密内容发给 B;B 用公钥 A 加密内容发给 A。

我的疑惑是: 看很多资料在解释Linux下两台主机ssh通信协商时会提到DH(diffie-hellman),我知道DH是密钥交换算法,可以使通信双方安全地产生一个公共密钥(对称密钥)。但是通过上述> 上述协商过程,A 和 B 不是已经可以利用 RSA 算法产生的公钥和私钥进行加密通信了吗,那为什么还需要 DH 算法呢? 难道上述过程之后还要用 DH 算法再生成一个公共密钥? ——知乎问题:SSH为什么要用到DH(Diffie-Hellman Exchange)?

首先要指出的是问题提出者所理解的 SSH 协商过程是错误的。

至于为什么不直接用 RSA 算法进行加密通信其实和 HTTS 差不多一个道理:RSA 算法是非对称加密算法,消耗资源多,运行效率低,不适合用于长连接下的数据交换加密。

如果想看此问题下的更多展开内容推荐 点击此链接查看第六篇参考文章 给出的回答。

客户端认证阶段

  1. 客户端向服务端发送认证请求;
  2. 服务端对客户端进行认证,如果认证失败则向客户端发送失败消息;
  3. 客户端可以选择再次认证知道达到认证次数上线(如果有设置的话)或认证成功位置。

常见的客户端认证方式有两种

  1. 密码认证:密码认证所用的账户密码一般与系统用户密码相同;
  2. 密钥认证:可接受公钥一般存放在用户目录下的 ~/.ssh/authorized_keys 中,注意需要 SSH 服务端拥有此文件的访问权限。

会话请求阶段

  1. 服务端等待客户端请求;
  2. 认证完成后,客户端向服务端发送会话请求;
  3. 服务端处理客户端请求:
    1. 完成后向客户端 SSH_SMSG_SUCCESS 报文,双方进入交互会话阶段;
    2. 如果请求未被成功处理,返回 SSH_SMSG_FAILURE 报文,表示请求处理失败或者不能识别客户端请求。

交互会话阶段

  1. 客户端将要执行的命令加密发送给服务端。
  2. 服务端收到后解密命令,执行后将结果加密返回客户端。
  3. 客户端将返回结果解密后显示到终端上。

OpenSSH

OpenSSH 是在 1999年 10月第一次在 OpenBSD 2.6 里出现,当初的项目是取代由 SSH Communications Security 所提供的 SSH 软件。 ——维基百科

程序主要包括了几个部分:

  1. ssh:SSH 客户端实现
  2. scp, sftp:rcp的替代方案,将文件复制到其他电脑上
  3. sshd:SSH 服务端实现
  4. ssh-keygen:产生 RSA 或 ECDSA 密钥,用来认证
  5. ssh-agent, ssh-add:帮助用户不需要每次输入密钥或密码的工具
  6. ssh-keysacn:收集大量主机的 ssh 主机公钥

ssh-keygen

常用选项:

选项

含义

作用

-t

type

指定要生成的密钥类型

-C

comment

提供一个注释

-b

bits

指定要生成的密钥长度(单位:bit)

-f

filename

指定生成的密钥文件名

ssh-agent 与 ssh-add

ssh-agent 是 OpenSSH 开发的用户提供 ssh 代理的工具,它可以为其他需要使用 ssh key 的程序提供代理。

ssh-add 是用来配合 ssh-agent 的,使用此工具可以向 ssh-agent 中添加私钥。

可以用过 ssh-add -l 查看已经添加的私钥列表。

参考

  1. 松鼠尚学堂:SSH 工作原理
  2. 运行的风:Linux SSH建立连接过程分析
  3. wchrt:ssh秘钥交换详解与实现…
  4. 月半兄:Diffie–Hellman 密钥协商算法详解
  5. Soulike:Diffie-Hellman 密钥交换算法及其安全性
  6. 知乎车小胖回答:SSH 为什么要用到 DH(Diffie-Hellman Exchange)?
  7. 坤哥玩csdn:关于ssh-keygen命令的介绍与用法

0 人点赞