作为一名服务器开发,每天都会使用到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的公钥来进行服务器身份的验证。接下来在通过协商使用强对称加密和散列算法,以确保在客户端和服务器之间交换的数据的私密性和完整性。
- RFC 4251 - The Secure Shell (SSH) Protocol Architecture
- RFC 4252 - The Secure Shell (SSH) Authentication Protocol
- RFC 4253 - The Secure Shell (SSH) Transport Layer Protocol
- RFC 4254 - The Secure Shell (SSH) Connection Protocol
1.1 SSH Transport Layer Protocol
SSH的传输层协议可作为基础的底层协议在一些安全网络服务中使用,它提供了强大的加密,服务器认证,数据完整性保护,以及数据压缩的功能。提供了包括:密钥交换算法,公钥算法(非对称加密算法),对称加密算法,消息认证算法,Hash算法的协商。SSH传输层协议中描述了Diffie-Hellman密钥交换算法,以及传输层协议规定的最小算法集合。
1.1.1 SSH传输层的协议结构
SSH传输层的协议结构有以下几部分组成:
- 连接设置。
- 底层采用TCP进行传输,服务器默认监听周知端口22端口。
- SSH协议版本号的交换。
- 不同协议版本的兼容。
- 二进制包协议。
1 | uint32 包长度 |
- 加密。加密算法和密钥是在密钥交换的过程中进行的,当加密生效后,协议数据必须全部进行加密。SSH建议加密密钥长度应该大于128位。下面是SSH协议列出的一些加密算法,其中3des-cbc是实现必须支持的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
163des-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 - 数据完整性
数据包的完整性是通过二进制包末尾的MAC消息认证码进行保证的,MAC值是通过共享密钥,包序号,包数据通过HASH算法计算得出。MAC算法和密钥是在密钥交换算法协商过程中得出的,所以,初始的包MAC数据是无效的,length必须为0。
1 | mac = MAC(key, sequence_number || unencrypted_packet) |
mac计算是使用包体的未加密数据,计算出mac值后,追加到包的结尾,然后再对整个包进行加密。SSH定义了如下MAC算法:
1 | hmac-sha1 REQUIRED HMAC-SHA1 (digest length = key length = 20) |
- 密钥交换算法
密钥交换算法决定了在一次会话中用于加密和认证的key该如何生成。文档要求了两种必须支持的密钥交换算法:
1 | diffie-hellman-group1-sha1 REQUIRED |
- 公钥算法
SSH传输层协议被设计为可以处理任何公钥格式,编码,算法,定义一个公钥类型需要考虑几个方面:密钥格式:
- 密钥如何编码,证书如何表示。
- 签名/加密算法:有些密钥不能同时支持签名和加密,
- 签名/加密数据的编码格式
SSH定义了以下几种公钥格式:
1 | ssh-dss REQUIRED sign Raw DSS Key |
公钥格式必须在协议中显示给出,证书/公钥的编码格式如下:
1
2string certificate or public key format identifier
byte[n] key/certificate data
- 算法协商
密钥交换算法的开始是通信双方交换自己支持的算法列表。各方会有一个首选算法,对于相同的首选算法进行采用,否则就需要遍历client支持的算法列表,找到第一个与server匹配的算法来使用(对于kex_algorithms还需要满足算法支持加密和签名的能力)。
1 | byte SSH_MSG_KEXINIT |
1.1.2 传输层建立过程WireShark分析
下面是传输层建立的过程:
在通过WireShark抓取SSH包的时候,一开始总是无法分析SSH协议,都是以TCP协议来展示,经过几番探索发现,原来WireShark的工作原理是对固定的熟知端口的协议进行分析,如果你的服务提供方换了端口,WireShark是不会进行对于的协议分析的,而我连接的server SSH的端口是配置成了26000的,需要在菜单:”Edit(编辑)->Preference(首选项)->Protocols->SSH”中的TCP ports中添加26000端口,表示关于此端口的通信,也采用SSH协议分析。
- 客户端发送自己的SSH协议版本号,SSH客户端
- 服务器回复自己的SSH协议版本号,SSH客户端,操作系统
- 开始算法协商,客户端发送自己期望和支持的各个算法列表
- 算法协商,服务器发送自己期望和支持的各个算法列表
- 通过ECDH算法进行密钥初始化-客户端发送自己的公钥
关于ECDH算法的秘钥如何协商可参考WIKI,
- 通过ECDH算法进行密钥初始化-服务器发送自己的公钥,并回复New Keys,表示后面的通信都必须使用新协商的秘钥进行对称加密
- 客户端回复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 | byte SSH_MSG_USERAUTH_REQUEST |
认证协议中密码以明文的形式存在,其安全性依赖于传输层通过协商的秘钥进行加密来实现。
1.2.2 公钥认证
公钥认证的方法主要用在自动化流程和系统管理员单点登陆。公钥认证方式在实际业界的使用非常广泛。公钥认证方式是SSH协议规定必须实现的。 公钥认证的协议格式如下:
1 | byte SSH_MSG_USERAUTH_REQUEST |
公钥认证事先需要客户端将公钥通过安全的方式存放到服务器端,在登陆认证的时候,客户端会用本地的私钥生成一个签名,发送给服务器,服务器通过公钥来解密签名。签名是需要私钥对以下数据进行处理生成的:
1 | string session identifier |
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
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