Chinaunix首页 | 论坛 | 博客
  • 博客访问: 18937823
  • 博文数量: 7460
  • 博客积分: 10434
  • 博客等级: 上将
  • 技术积分: 78178
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-02 22:54
文章分类

全部博文(7460)

文章存档

2011年(1)

2009年(669)

2008年(6790)

分类: 网络与安全

2008-05-31 21:25:10

第一部分:协议概览
  
  整个通讯过程中,经过下面几个阶段协商实现认证连接。
  
  第一阶段:
  
  由客户端向服务器发出 TCP 连接请求。TCP 连接建立后,客户端进入等待,服务器向客户端发送第一个报文,宣告自己的版本号,包括协议版本号和软件版本号。协议版本号由主版本号和次版本号两部分组成。它和软件版本号一起构成形如:
  
  "SSH-<主协议版本号>.<次协议版本号>-<软件版本号>\n"
  
  的字符串。其中软件版本号字符串的最大长度为40个字节,仅供调试使用。客户端接到报文后,回送一个报文,内容也是版本号。客户端响应报文里的协议版本号这样来决定:当与客户端相比服务器的版本号较低时,如果客户端有特定的代码来模拟,则它发送较低的版本号;如果它不能,则发送自己的版本号。当与客户端相比服务器的版本号较高时,客户端发送自己的较低的版本号。按约定,如果协议改变后与以前的相兼容,主协议版本号不变;如果不相兼容,则主主协议版本号升高。
  
  服务器接到客户端送来的协议版本号后,把它与自己的进行比较,决定能否与客户端一起工作。如果不能,则断开TCP 连接;如果能,则按照二进制数据包协议发送第一个二进制数据包,双方以较低的协议版本来一起工作。到此为止,这两个报文只是简单的字符串,你我等凡人直接可读。
  
  第二阶段:
  
  协商解决版本问题后,双方就开始采用二进制数据包进行通讯。由服务器向客户端发送第一个包,内容为自己的 RSA主机密钥(host key)的公钥部分、RSA服务密钥(server key)的公钥部分、支持的加密方法、支持的认证方法、次协议版本标志、以及一个 64 位的随机数(cookie)。这个包没有加密,是明文发送的。客户端接收包后,依据这两把密钥和被称为cookie的 64 位随机数计算出会话号(session id)和用于加密的会话密钥(session key)。随后客户端回送一个包给服务器,内容为选用的加密方法、cookie的拷贝、客户端次协议版本标志、以及用服务器的主机密钥的公钥部分和服务密钥的公钥部分进行加密的用于服务器计算会话密钥的32 字节随机字串。除这个用于服务器计算会话密钥的 32字节随机字串外,这个包的其他内容都没有加密。之后,双方的通讯就是加密的了,服务器向客户端发第二个包(双方通讯中的第一个加密的包)证实客户端的包已收到。
  
  第三阶段:
  
  双方随后进入认证阶段。可以选用的认证的方法有:
  
  (1) ~/.rhosts 或 /etc/hosts.equiv 认证(缺省配置时不容许使用它);
  (2) 用 RSA 改进的 ~/.rhosts 或 /etc/hosts.equiv 认证;
  (3) RSA 认证;
  (4) 口令认证。
  
  如果是使用 ~/.rhosts 或 /etc/hosts.equiv 进行认证,客户端使用的端口号必须小于1024。
  
  认证的第一步是客户端向服务器发 SSH_CMSG_USER 包声明用户名,服务器检查该用户是否存在,确定是否需要进行认证。如果用户存在,并且不需要认证,服务器回送一个SSH_SMSG_SUCCESS 包,认证完成。否则,服务器会送一个 SSH_SMSG_FAILURE 包,表示或是用户不存在,或是需要进行认证。注意,如果用户不存在,服务器仍然保持读取从客户端发来的任何包。除了对类型为 SSH_MSG_DISCONNECT、SSH_MSG_IGNORE 以及 SSH_MSG_DEBUG 的包外,对任何类型的包都以 SSH_SMSG_FAILURE 包。用这种方式,客户端无法确定用户究竟是否存在。
  
  如果用户存在但需要进行认证,进入认证的第二步。客户端接到服务器发来的 SSH_SMSG_FAILURE 包后,不停地向服务器发包申请用各种不同的方法进行认证,直到时限已到服务器关闭连接为止。时限一般设定为 5 分钟。对任何一个申请,如果服务器接受,就以 SSH_SMSG_SUCCESS 包回应;如果不接受,或者是无法识别,则以 SSH_SMSG_FAILURE 包回应。
  
  第四阶段:
  
  认证完成后,客户端向服务器提交会话请求。服务器则进行等待,处理客户端的请求。在这个阶段,无论什么请求只要成功处理了,服务器都向客户端回应 SSH_SMSG_SUCCESS包;否则回应 SSH_SMSG_FAILURE 包,这表示或者是服务器处理请求失败,或者是不能识别请求。会话请求分为这样几类:申请对数据传送进行压缩、申请伪终端、启动 X11、TCP/IP 端口转发、启动认证代理、运行 shell、执行命令。到此为止,前面所有的报文都要求 IP 的服务类型(TOS)使用选项 IPTOS_THROUGHPUT。
  
  第五阶段:
  
  会话申请成功后,连接进入交互会话模式。在这个模式下,数据在两个方向上双向传送。此时,要求 IP 的服务类型(TOS)使用 IPTOS_LOWDELAY 选项。当服务器告知客户端自己的退出状态时,交互会话模式结束。
  
  (注意:进入交互会话模式后,加密被关闭。在客户端向服务器发送新的会话密钥后,加密重新开始。用什么方法加密由客户端决定。)
  
  第二部分:数据包格式和加密类型
  
  二进制数据包协议:
  
  包 = 包长域(4字节:u_int32_t) + 填充垫(1-7字节)
  + 包类型域(1字节:u_char) + 数据域
  + 校验和域(4字节)
  加密部分 = 填充垫 + 包类型 + 数据 + 校验和
  包长 = 1(包类型) + 数据字节长度 + 4(校验和)
  
  数据包压缩:
  
  如果支持压缩,包类型域和数据域用 gzip 压缩算法进行压缩。压缩时在两个数据传送方向的任何一个上,包的压缩部分(类型域+数据域)被构造得象是它连在一起,形成一个连续的数据流。在两个数据传送方向上,压缩是独立进行的。
  
  数据包加密:
  
  现时支持的数据加密方法有这样几种:
  
  SSH_CIPHER_NONE 0 不进行加密
  SSH_CIPHER_IDEA 1 IDEA 加密法(CFB模式)
  SSH_CIPHER_DES 2 DES 加密法(CBC模式)
  SSH_CIPHER_3DES 3 3DES 加密法(CBC模式)
  SSH_CIPHER_ARCFOUR 5 Arcfour加密法)
  SSH_CIPHER_BLOWFISH 6 Blowfish 加密法
  
  协议的所有具体实现都要求支持3DES。
  
  DES 加密:
  
  从会话密钥中取前8个字节,每个字只用高7位,忽略最低位,这样构成56位的密钥供加密使用。加密时使用CBC 模式,初使矢量被初始化为全零。
  
  3DES 加密:
  
  3DES 是 DES 的变体,它三次独立地使用 CBC 模式的DES 加密法,每一次的初始矢量都是独立的。第一次用DES 加密法对数据进行加密;第二次对第一次加密的结果用 DES 加密法进行解密;第三次再对第二次解密的
  结用 DES 加密法进行加密。注意:第二次解密的结果并不就是被加密的数据,因为三次使用的密钥和初始矢量都是分别不同的。与上面的 DES 加密采用的方法类似,第一次从会话密钥中取起始的前8个字节生成加密密钥,第二次取下一个紧跟着的8个字节,第三次取再下一个紧跟着的8个字节。三次使用的初始矢量都初始化为零。
  
  IDEA 加密:
  
  加密密钥取自会话密钥的前16个字节,使用 CFB 模式。初始矢量初始化为全零。
  
  RC4 加密:
  
  会话密钥的前16个字节被服务器用作加密密钥,紧接着的下一个16字节被客户端用作加密密钥。结果是两个数据流方向上有两个独立的129位密钥。这种加密算法非常快。
  
  第二部分:密钥的交换和加密的启动
  
  在服务器端有一个主机密钥文件,它的内容构成是这样的:
  
  1. 私钥文件格式版本字符串;
  2. 加密类型(1 个字节);
  3. 保留字(4 个字节);
  4. 4 个字节的无符号整数;
  5. mp 型整数;
  6. mp 型整数;
  7. 注解字符串的长度;
  8. 注解字符串;
  9. 校验字(4 个字节);
  10. mp 型整数;
  11. mp 型整数;
  12. mp 型整数;
  13. mp 型整数;
  
  其中 4、5、6 三个字段构成主机密钥的公钥部分;10、11、12、13 四个字段构成主机密钥的私钥部分。9、10、11、12、13 五个字段用字段 2 的加密类型标记的加密方法进行了加密。4 个字节的校验字交叉相等,即第一个字节与第三个字节相等,第二个字节与第四个字节相等。在服务器读取这个文件时进行这种交叉相等检查,如果不满足这个条件,则报错退出。
  
  服务器程序运行的第一步,就是按照上面的字段划分读取主机密钥文件。随后生成一个随机数,再调用函数
  
  void rsa_generate_key
  (
  RSAPrivateKey *prv,
  RSAPublicKey *pub,
  RandomState *state,
  unsigned int bits
  );
  
  生成服务密钥,服务密钥也由公钥和私钥两部分组成。上面的这个函数第一个指针参数指向服务密钥的私钥部分,第二个指向公钥部分。然后把主机密钥的公钥部分和服务密钥的公钥部分发送给客户端。在等到客户端回应的包后,服务器用自己的主机密钥的私钥部分和服务密钥的私钥部分解密得到客户端发来的 32 字节随机字串。然后计算自己的会话号,并用会话号的前 16字节 xor 客户端发来的 32 字节随机字串的前 16 字节,把它作为自己的会话密钥。注意,服务器把8个字节的 cookie、主机密钥的公钥部分、和服务密钥的公钥部分作为参数来计算自己的会话号。
  
  再来看客户端。客户端启动后的第一步骤也是读取主机密钥。然后等待服务器主机密钥、服务密钥、和 8个字节的cookie。注意,服务器发送来的只是主机密钥和服务密钥的公钥部分。接到包后,客户端立即把从服务器端收到cookie、主机密钥、和服务密钥作为参数计算出会话号。从上面可以看出,服务器和客户端各自计算出的会话号实际是一样的。
  
  随后,客户端检查用户主机列表和系统主机列表,查看从服务器收到的主机密钥是否在列表中。如果不在列表
【责编:Yoyo】
阅读(315) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~