SSH协议和原理浅析

  1. 1 SSH协议工作原理
    1. 1.1 SSH Transport Layer Protocol
      1. 1.1.1 SSH传输层的协议结构
      2. 1.1.2 传输层建立过程WireShark分析
    2. 1.2 SSH Authentication Protocol
      1. 1.2.1 口令认证
      2. 1.2.2 公钥认证
      3. 1.2.3 基于主机的认证
  2. 2. 总结
  3. 3. 参考

作为一名服务器开发,每天都会使用到SSH,我们从第一次接触服务器的时候就接触了SSH,但我们真的了解SSH吗,最近在使用github的private repo的时候发现只有使用公私钥的验证方式才能clone repo到本地,由此决定好好整理一下SSH的设计原理和协议细节,让自己对SSH有一个深入的认识。

下面是SSH WIKI的关于SSH的解释:

Secure Shell(安全外壳协议,简称SSH)是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境。SSH通过在网络中创建安全隧道来实现SSH客户端与服务器之间的连接。虽然任何网络服务都可以通过SSH实现安全传输,SSH最常见的用途是远程登录系统,人们通常利用SSH来传输命令行界面和远程执行命令。使用频率最高的场合类Unix系统。

SSH是一个安全传输协议标准,它提供了多种方式来进行安全认证,以保证通信的安全和完整。以替代Telnet,rlogin,rsh,ftp等明文传输协议,防止被嗅探,中间人攻击。

1 SSH协议工作原理

SSH协议的工作方式是采用CS模式,SSH client通过和SSH server建立链接来进行工作。SSH client发起链接,并通过SSH server的公钥来进行服务器身份的验证。接下来在通过协商使用强对称加密和散列算法,以确保在客户端和服务器之间交换的数据的私密性和完整性。

SSH链接建立流程
Tatu Ylonen在SSH协议流行后,将其转交到了IETF进行标准化,作为一个互联网标准,SSH协议有以下几个RFC文档组成:

1.1 SSH Transport Layer Protocol

SSH的传输层协议可作为基础的底层协议在一些安全网络服务中使用,它提供了强大的加密,服务器认证,数据完整性保护,以及数据压缩的功能。提供了包括:密钥交换算法,公钥算法(非对称加密算法),对称加密算法,消息认证算法,Hash算法的协商。SSH传输层协议中描述了Diffie-Hellman密钥交换算法,以及传输层协议规定的最小算法集合。

1.1.1 SSH传输层的协议结构

SSH传输层的协议结构有以下几部分组成:

  1. 连接设置。
  • 底层采用TCP进行传输,服务器默认监听周知端口22端口。
  • SSH协议版本号的交换。
  1. 不同协议版本的兼容。
  2. 二进制包协议。
1
2
3
4
5
uint32    包长度
byte 随机填充数据长度
byte[n1] 有效数据; n1 = 包长度 - 随机填充数据长度 - 1
byte[n2] 随机填充数据; n2 = 随机填充数据长度
byte[m] 消息认证码(Message Authentication Code); m = mac_length
  1. 加密。加密算法和密钥是在密钥交换的过程中进行的,当加密生效后,协议数据必须全部进行加密。SSH建议加密密钥长度应该大于128位。下面是SSH协议列出的一些加密算法,其中3des-cbc是实现必须支持的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    3des-cbc         REQUIRED          three-key 3DES in CBC mode
    blowfish-cbc OPTIONAL Blowfish in CBC mode
    twofish256-cbc OPTIONAL Twofish in CBC mode, with a 256-bit key
    twofish-cbc OPTIONAL alias for "twofish256-cbc"
    twofish192-cbc OPTIONAL Twofish with a 192-bit key
    twofish128-cbc OPTIONAL Twofish with a 128-bit key
    aes256-cbc OPTIONAL AES in CBC mode, with a 256-bit key
    aes192-cbc OPTIONAL AES with a 192-bit key
    aes128-cbc RECOMMENDED AES with a 128-bit key
    serpent256-cbc OPTIONAL Serpent in CBC mode, with a 256-bit key
    serpent192-cbc OPTIONAL Serpent with a 192-bit key
    serpent128-cbc OPTIONAL Serpent with a 128-bit key
    arcfour OPTIONAL the ARCFOUR stream cipherwith a 128-bit key
    idea-cbc OPTIONAL IDEA in CBC mode
    cast128-cbc OPTIONAL CAST-128 in CBC mode
    none OPTIONAL no encryption; NOT RECOMMENDED
  2. 数据完整性

数据包的完整性是通过二进制包末尾的MAC消息认证码进行保证的,MAC值是通过共享密钥,包序号,包数据通过HASH算法计算得出。MAC算法和密钥是在密钥交换算法协商过程中得出的,所以,初始的包MAC数据是无效的,length必须为0。

1
mac = MAC(key, sequence_number || unencrypted_packet)

mac计算是使用包体的未加密数据,计算出mac值后,追加到包的结尾,然后再对整个包进行加密。SSH定义了如下MAC算法:

1
2
3
4
5
hmac-sha1    REQUIRED        HMAC-SHA1 (digest length = key length = 20)
hmac-sha1-96 RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
hmac-md5 OPTIONAL HMAC-MD5 (digest length = key length = 16)
hmac-md5-96 OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
none OPTIONAL no MAC; NOT RECOMMENDED
  1. 密钥交换算法

密钥交换算法决定了在一次会话中用于加密和认证的key该如何生成。文档要求了两种必须支持的密钥交换算法:

1
2
diffie-hellman-group1-sha1 REQUIRED
diffie-hellman-group14-sha1 REQUIRED
  1. 公钥算法

SSH传输层协议被设计为可以处理任何公钥格式,编码,算法,定义一个公钥类型需要考虑几个方面:密钥格式:

  • 密钥如何编码,证书如何表示。
  • 签名/加密算法:有些密钥不能同时支持签名和加密,
  • 签名/加密数据的编码格式

SSH定义了以下几种公钥格式:

1
2
3
4
ssh-dss           REQUIRED     sign   Raw DSS Key
ssh-rsa RECOMMENDED sign Raw RSA Key
pgp-sign-rsa OPTIONAL sign OpenPGP certificates (RSA key)
pgp-sign-dss OPTIONAL sign OpenPGP certificates (DSS key)

公钥格式必须在协议中显示给出,证书/公钥的编码格式如下:

1
2
string    certificate or public key format identifier
byte[n] key/certificate data

  1. 算法协商

密钥交换算法的开始是通信双方交换自己支持的算法列表。各方会有一个首选算法,对于相同的首选算法进行采用,否则就需要遍历client支持的算法列表,找到第一个与server匹配的算法来使用(对于kex_algorithms还需要满足算法支持加密和签名的能力)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
byte         SSH_MSG_KEXINIT
byte[16] cookie (random bytes)
name-list kex_algorithms
name-list server_host_key_algorithms
name-list encryption_algorithms_client_to_server
name-list encryption_algorithms_server_to_client
name-list mac_algorithms_client_to_server
name-list mac_algorithms_server_to_client
name-list compression_algorithms_client_to_server
name-list compression_algorithms_server_to_client
name-list languages_client_to_server
name-list languages_server_to_client
boolean first_kex_packet_follows
uint32 0 (reserved for future extension)

1.1.2 传输层建立过程WireShark分析

下面是传输层建立的过程:

在通过WireShark抓取SSH包的时候,一开始总是无法分析SSH协议,都是以TCP协议来展示,经过几番探索发现,原来WireShark的工作原理是对固定的熟知端口的协议进行分析,如果你的服务提供方换了端口,WireShark是不会进行对于的协议分析的,而我连接的server SSH的端口是配置成了26000的,需要在菜单:”Edit(编辑)->Preference(首选项)->Protocols->SSH”中的TCP ports中添加26000端口,表示关于此端口的通信,也采用SSH协议分析。

  1. 客户端发送自己的SSH协议版本号,SSH客户端
  1. 服务器回复自己的SSH协议版本号,SSH客户端,操作系统
  1. 开始算法协商,客户端发送自己期望和支持的各个算法列表
  1. 算法协商,服务器发送自己期望和支持的各个算法列表
  1. 通过ECDH算法进行密钥初始化-客户端发送自己的公钥

关于ECDH算法的秘钥如何协商可参考WIKI

  1. 通过ECDH算法进行密钥初始化-服务器发送自己的公钥,并回复New Keys,表示后面的通信都必须使用新协商的秘钥进行对称加密
  1. 客户端回复New Keys 消息,表示后面的通信都必须使用新协商的秘钥进行对称加密

在双方都发送了SSH_MSG_NEWKEYS后,客户端和服务器都收到了对方的公钥,双方利用自己的私钥,对方公钥和原始共享素数来计算共享密钥。虽然这个过程各自独立,都使用自己私钥和对方公钥,所以能生成相同共享密钥。后面的协议数据都是用新协商的Key进行加密。

但我们要知道,无论是DH还是ECDH秘钥交换协议都没有验证对方的身份,因此无法阻止中间人攻击,中间人可以同时截获通信双方的公钥,然后对双方的消息进行解密,用自己的秘钥进行加密而不被通信双方察觉。为了解决这种问题,需要在交换公钥前对公钥进行签名:

  • 用安全的信道传输数字签名的秘钥;
  • 使用CA提供可信的数字签名秘钥;

1.2 SSH Authentication Protocol

传输层建立链接协商完秘钥后,客户端收到服务器的Host key和其摘要,第一次连接服务器,会显示host key fingerprint,需要人为判断该host key fingerprint是否是真实的服务器。确认后会将该服务器加入 ~/.ssh/know_hosts中。如下图所示:

传输层建立后,进入身份认证流程,如何进行用户的身份认证(client和server都需要认证),保证不会被中间人攻击。SSH提供了多种方式来进行用户认证,其中最常用的两种方式是:口令认证公钥认证。公钥认证比口令认证更安全,至于为什么,后面你就会知道!~

1.2.1 口令认证

口令认证机制,是我们在个人开发中比较常用的一种方式,它的使用前提是假设在server没有被攻破。假设服务器被攻击,使用口令认证的方式,会导致用户传递的用户名和密码被攻击者获取,造成很严重的后果。口令认证协议如下:

1
2
3
4
5
6
byte      SSH_MSG_USERAUTH_REQUEST
string user name
string service name
string "password"
boolean FALSE
string plaintext password in ISO-10646 UTF-8 encoding [RFC3629]

认证协议中密码以明文的形式存在,其安全性依赖于传输层通过协商的秘钥进行加密来实现

1.2.2 公钥认证

公钥认证的方法主要用在自动化流程系统管理员单点登陆。公钥认证方式在实际业界的使用非常广泛。公钥认证方式是SSH协议规定必须实现的。 公钥认证的协议格式如下:

1
2
3
4
5
6
7
byte      SSH_MSG_USERAUTH_REQUEST
string user name in ISO-10646 UTF-8 encoding [RFC3629]
string service name in US-ASCII
string "publickey"
boolean FALSE
string public key algorithm name
string public key blob

公钥认证事先需要客户端将公钥通过安全的方式存放到服务器端,在登陆认证的时候,客户端会用本地的私钥生成一个签名,发送给服务器,服务器通过公钥来解密签名。签名是需要私钥对以下数据进行处理生成的:

1
2
3
4
5
6
7
8
string    session identifier
byte SSH_MSG_USERAUTH_REQUEST
string user name
string service name
string "publickey"
boolean TRUE
string public key algorithm name
string public key to be used for authentication

1.2.3 基于主机的认证

一些站点希望允许在特定主机上登陆远程主机的认证方式。这样不符合高的安全等级,但他确实很方便。这是一个可选的认证方式,在实现的时候一定要注意防止一个普通的用户窃取服务器的private host key

2. 总结

其实SSH协议本身规定了一组加密算法套件的使用,完备的加密算法套件包括了一组算法,包括密钥交换算法,认证算法,加密算法,完整性校验算法,如下:

  • 密钥交换算法:负责协商对称密钥,常见类型包括 RSA、DH、ECDH、ECDHE 等;
  • 认证算法:用来负责身份的验证,都是非对称加密算法,常见有:RSA,DSA,ESDSA等;
  • 加密数据算法:对建立连接的通信内容进行对称加密,常见类型包括 AES 等;
  • 消息认证信息码(MAC)算法:创建报文摘要,验证消息的完整性,常见类型包括 SHA 等;

我们可以看出,这些算法在SSH传输层建立的算法协商过程中都会确认到,整个SSH通信过程中都会使用到这些算法套件。同样HTTPS的TLS算法套件的结构也是这样。

3. 参考

我的第一个使用的ssh client版本SSH Secure Shell Client

官方推荐的ssh client

https://www.ssh.com/ssh/protocol/

Why Authentication Using SSH Public Key Is Better Than Using Password And How Do They Work?

https://www.phcomp.co.uk/Tutorials/Unix-And-Linux/ssh-check-server-fingerprint.html

重新认识SSH

加解密算法

ssh工作原理

ssh工作原理

ECDH秘钥协商算法原理