SSH2.0编程 ssh协议过程实现

 

    之前为了自己做一套SSH,先自己实现了一套telnet。但经过这么多天的苦逼,发现以前的工作都是徒劳。ssh的协议很繁杂,核心的内容在于密码算法,而且自己很难在网上找到周全的细节讲解与详细的实现,只有靠自己刷RFC和问大神还有就是靠强X我的服务器艰难地完成。

    现计算了下时间,自己做SSH耗费了进两个月的时间,虽然期间也夹着其他的繁杂事物,但自己在这方面确是是耗费了非常大的精力。因为这方面详细资料的匮乏,自己以前也几乎没有接触过密码学方面的东西,很多只有靠自己摸索,所以我得经常拿我自己的服务器来做黑盒测试,我现在服务器上的ss服务器日志全是一些非法连接的记录(—_—|||)。早知当初就不那么作死非要自己实现他的加密算法和过程,用openssl就很快搞定了。但我还是觉得这次做SSH的精力是我受益匪浅,不仅熟悉了各种加密,并且能靠自己实现并熟练应用了。可能这些对自己帮助不大,但至少和信安的小伙伴也有点吹牛的谈资了~

    这篇文章希望能帮助到想了解ssh2.0协议或是亲手实现ssh协议的小伙伴。

 

    首先对数据包的格式进行说明:

    数据包由包长度(Packet Length)、填充长读(Padding Length)、信息代码(Msg code)、信息内容与填充值(Padding String) 这5部分组成。信息内容中的一些字符串以4字节长度+该长度数量的字符组成,数值按照网络序排列,例如:abc: 00 00 00 03 (char)a (char)b (char)c 。另外有一种大整数的情况,负数和字符串的表示方式一样,正数需要前导0,例如 4b64: 00 00 00 03 00 4b 64 。

    ssh头的结构体:

1
2
3
4
5
6
7
struct  sshhead
{
     unsigned  int  tlen;
     unsigned  char  plen;
     unsigned  char  msgcode;
     sshhead(){tlen=6;}
};

 

    就拿通过ssh远程控制的一个完整个过程来讲,ssh的过程可分为以下3部分:

    一、版本协商

    二、算法协商与密钥交换

    三、加密通信(可能含有2、3部分)

    这其中第二部分是ssh最为核心的过程,该过程决定了以后通信所要使用的密钥,下面按顺序对每个部分对比着数据包进行详细的讲解并给出实现的过程。

    

    一、版本协商:

    在建立连接后,客户端与服务器分别向对方发送自己ssh的版本信息(这里的数据格式不同于其他包,只有一行版本号),以\r\n结束。版本的格式如下: 

                         SSH-ssh协议版本-详细版本\r\n (几乎只有ssh协议版本之前的信息有效)

    比如我linux上的就是:SSH-2.0-OpenSSH_5.3\r\n

    Putty的是:          SSH-2.0-PuTTY_Release_0.63\r\n

    

    一般来说,在建立连接后,是先由服务器发版本号过来,单线程处理版本协商的朋友需要注意下。

    在双方收到对方发来的版本号后,会根据两者之中最小的版本来进行接下来的通讯。

    

    二、算法协商与秘钥交换:

    这部分的内容将会占该文章总篇幅的一半以上。

    首先给大家看下整个过程的数据包大概:

        整个部分是从第6条开始到第15条结束,除去中间的非协议部分,总共有7条数据包。看起来只有这么几条数据包,但其中包含了非常多的过程与隐秘的信息。

    1、算法协商:

        位第6、9数据,分别为双发向对法发送的自己在不同密码需求上支持的算法。

     该数据包的格式:

    按顺序分别是:

        cookie(随机的值,16byte)

        kex_algorithms(秘钥租交换算法)

        server_host_key_algorithms(服务器主机秘钥,正常情况用处不大,甚至可以不用)

        encryption_algorithms_client_to_server(两端通信使用的加密算法)

        encryption_algorithms_server_to_client

        mac_algorithms_client_to_server(数据校验用的hash算法)

        mac_algorithms_server_to_client

        compression_algorithms_client_to_server(压缩算法)

        compression_algorithms_server_to_client

        languages_client_to_server

        languages_server_to_client

        first_kex_packet_follows

        0(4byte整数,扩展用的)

    每个算法类型可能会有多个不同的算法,这些算法之间使用逗号隔开。

    现在双方知道对方支持的算法,但是应该怎样决定每个类型实际所使用的算法呢?

    每个算法类型列表的第一个算法必须是首选的算法,服务器应以客户端的算法优先级作为考虑,就拿交换算法举例:

        现在服务器有三个算法dh1,dh2,dh3

            客户端有两个算法dh3,dh2

        那么服务器的首选算法是dh1,而客户端是dh3,客户端此时知道服务器有dh3算法,因此客户端就确认使用dh3算法。服务器发现自己的首选算法与客户端不同,而自己拥有客户端的首选算法,因此服务器也确认使用dh3算法。

        再看另一个情况

            服务器:dh1,dh2,dh3

            客户端:dh4,dh3,dh1

        这时服务器没有客户端的首选算法,客户端会使用第二个算法dh3,此时服务器也支持第二个算法,双方将确定使用dh3算法。

    如果服务器和客户端双方没有共同的算法,这次会话将会终止。

    下面是代码实现和服务器之间的版本协商

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define KEI     (char)20
#define NK      (char)21
//算法名
#define VER "SSH-2.0-WCHRT_1.0\r\n"
#define COOKIE "0123456789ABCDEF"
#define VKEX "diffie-hellman-group-exchange-sha256"
#define VSHK "ssh-rsa"
#define VECS "aes128-cbc"
#define VESC "aes128-cbc"
#define VMCS "hmac-sha1"
#define VMSC "hmac-sha1"
#define VCCS "none"
#define VCSC "none"
#define VLCS ""
#define VLSC ""
#define KFPF ""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
bool  key_exchange()
{
     sshhead sshh;
     sshh.msgcode=KEI;
     sshh.tlen-=4;
     //将算法列表与信息分别写入缓冲区
     mstrin(COOKIE,sshh.tlen);
     mstrin(VKEX,sshh.tlen);
     mstrin(VSHK,sshh.tlen);
     mstrin(VECS,sshh.tlen);
     mstrin(VESC,sshh.tlen);
     mstrin(VMCS,sshh.tlen);
     mstrin(VMSC,sshh.tlen);
     mstrin(VCCS,sshh.tlen);
     mstrin(VCSC,sshh.tlen);
     mstrin(VLCS,sshh.tlen);
     mstrin(VLSC,sshh.tlen);
     //ed
     for ( int  i=0;i<5;sshh.tlen++,i++)
     {
         data[sshh.tlen]=( char )0;
     }
//载荷的计算与总长度的写入都放在最后
     //count padding length
     count_padding(sshh);
     sshheadin(sshh);
 
     //没有封装socket
     len=send(sock,data,sshh.tlen+4,0);
     mrecv(10);
 
     //printf("(%d)",len);
     if (data[5]==KEI)
     {
         return  true ;
     }
 
     return  false ;
}

用到的一些功能函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//向缓冲区填充字符串,长度使用网络字节序
void  mstrin(string s,unsigned  int  &tlen)
{
     data[tlen]=( char )(s.length()/(256*256*256));
     data[tlen+1]=( char )(s.length()/(256*256));
     data[tlen+2]=( char )(s.length()/256);
     data[tlen+3]=( char )(s.length());
     tlen+=4;
     for ( int  i=0;i<s.length();tlen++,i++)
     {
         data[tlen]=s[i];
     }
     data[tlen]= '\0' ;
}
 
void  sshheadin(sshhead &sshh)
{
     sshh.tlen-=4;
     data[0]=( char )(sshh.tlen/(256*256*256));
     data[1]=( char )(sshh.tlen/(256*256));
     data[2]=( char )(sshh.tlen/256);
     data[3]=( char )(sshh.tlen);
     data[4]=( char )sshh.plen;
     data[5]=sshh.msgcode;
}
 
void  count_padding(sshhead &sshh)
{
     int  k=2;
     if (sshh.tlen%8<4)
     {
         k=1;
     }
     sshh.plen=(sshh.tlen/8+k)*8-sshh.tlen;
     sshh.tlen=(sshh.tlen/8+k)*8;
}

    2、秘钥交换

        在算法协商成功过后,双方便立马进行秘钥组的交换。ssh2.0版本所使用的秘钥组交换协议算法主要使用diffie-hellman-group-exchange-sha算法。

         鉴于该部分内容特别多,我特意在另一篇单独的文章中予以详细介绍,再阅读下文前请先参考该文章:dh-gex-sha算法详解

        我们数据包的第10到15条都是该部分的内容

        

    1、dh key exchange init (C)

        密钥交换初始化,由客户端先向服务器发送秘钥交换请求的数据包,告知开始秘钥交换。

        

1
2
3
4
5
6
7
8
9
10
11
12
13
14
         //<<<<<<<<<<DH KEX INIT<<<<<<<<<
     sshh.tlen=6;
     sshh.msgcode=DHKEI;
     //payload
     mintin(0x1000,sshh.tlen);
     count_padding(sshh);
     sshheadin(sshh);
 
     len=send(sock,data,sshh.tlen+4,0);
     
     //dh: set I_C
     dhdata.set_i_c(string(data+4,sshh.tlen));
     //dhdata.set_i_c(string(data,len));
     //>>>>>>>>>>>>>>>>>>>>>>>>

 

     2、dh key exchange reply  (S)

        服务器收到客户端发起交换的请求后,将自己用于dh算法的P、G发送给客户端,用于客户端生成dh公私钥。这里的P是一个大素数,而G是大于1的数,G不必过大,10位以内最后,因为按幂运算G能轻易生成特别大的数。

        

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
         //<<<<<<<<<<DH KEX REPLAY<<<<<<<<<
     mrecv(10);
     
     if (data[5]!=DHKER)
     {
         puts ( "DH KEX REPLAY error" );
         return  false ;
     }
     
     //dh: set I_S
     dhdata.set_i_s(string(data+4,len-4));
     //dhdata.set_i_s(string(data,len));
 
     //dh: read P
     pos=6;
     intlen=readstrint(data+pos);
     pos+=4;
     Integer p=readstrbigint(data+pos,intlen);
     pos+=intlen;
     //dh: read G
     intlen=readstrint(data+pos);
     pos+=4;
     Integer g=readstrbigint(data+pos,intlen);
     pos+=intlen;
     //dh: set G and P
     dhdata.set_g_and_p(g,p);
     //cout<<dhdata.dh_p<<" "<<dhdata.dh_g<<endl;
     //>>>>>>>>>>>>>>>>>>>>>>>>

 

       3、dh gex init   (C)

        客户端收到服务器发过来的P、G后,自己变成根据P、G生成并计算出自己的公钥e。这一步也只需要客户端将生成的e发送给服务器即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//<<<<<<<<<<DH GEX INIT<<<<<<<<<
     sshh.tlen=6;
     sshh.msgcode=DHGI;
 
     dhdata.comp_e();
     string e=inttostr(dhdata.get_e(),256);
     mstrin(e,sshh.tlen);
     count_padding(sshh);
     sshheadin(sshh);
 
     len=send(sock,data,sshh.tlen+4,0);
 
//debugstr(data,len);
     //>>>>>>>>>>>>>>>>>>>>>>>>

        

 

        4、dhgex reply   (S)

        重要的来了,服务器收到了客户端发来的e后,便能计算出共享秘钥K,并根据现有信息计算出生成所需秘钥的H。

        这个数据包里面含有如下信息:

        KEX DH host key(K_S):

            主机公钥,一般为rsa公钥。完整的格式为:总长度+算法名长度+算法名+证书(n)长度+证书(n)+公钥长度+公钥。

        DH server f :

            服务器的dh公钥值,客户端收到后便能用f计算出同样的共享秘钥K。

        KEX DH H signature (签名后的H):

            服务器用主机私钥对计算出的hash值H进行签名的结果。格式为:总长度+算法名长度+算法名+签名数据长度+签名值。

            H的计算方法: H=hash(V_C||V_S||I_C||I_S||K_S||e||f||K);

             按顺序用到的值(注意类型):

    

类型 说明
string V_C 客户端的初始报文(版本信息:SSH-2.0-xxx,不含结尾的CR和LF)
string V_S 服务器的初始报文
string I_C 客户端 SSH_MSG_KEX_INIT的有效载荷(不含开头的数据长度值)
string I_S 服务器的同上
string K_S 主机秘钥(dh gex reply(33)过程服务器发送host key (RSA公钥))
mpint e 客户端DH公钥
mpint f 服务器DH公钥
mpint K 共同DH计算结果

        

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
         //<<<<<<<<<<DH GEX REPLAY<<<<<<<<<
     mrecv(10);
     if (data[5]!=DHGR)
     {
         puts ( "DH GEX REPLAY error" );
         system ( "pause" );
         return  false ;
     }
     int  padlen=data[4];
     //dh: set server host key
     pos=6;
     intlen=readstrint(data+pos); //host key all length
     dhdata.set_k_s(string(data+pos+4,intlen));
     pos+=4;
 
     intlen=readstrint(data+pos); //host key name
     pos+=4;
     pos+=intlen;
     intlen=readstrint(data+pos); //get rsa e and n
     pos+=4;
     Integer ee=readstrbigint(data+pos,intlen);
     pos+=intlen;
     intlen=readstrint(data+pos);
     pos+=4;
     Integer nn=readstrbigint(data+pos,intlen); //set rsa e and n
     pos+=intlen;
     dhdata.set_e_and_n(ee,nn);
 
     //dh: set dh server f
     intlen=readstrint(data+pos);
     pos+=4;
     dhdata.set_f(readstrbigint(data+pos,intlen));
     pos+=intlen;
     //dh: set shka_name
     pos+=4; //h's total length
     intlen=readstrint(data+pos);
     pos+=4;
     dhdata.set_shka_name(string(data+pos,intlen));
     pos+=intlen;
     //dh: set server h
     intlen=readstrint(data+pos);
     pos+=4;
     dhdata.set_s_h(string(data+pos,intlen));
     pos+=intlen;
     pos+=padlen;
     //and other MAC//
 
     dhdata.comp_k();
     dhdata.comp_h();

 

        

 

         5、new keys   (C)

         

          客户端收到服务器的信息后计算出K,并用同样的方式计算出H(服务器和客户端的H都是同一个值)。并使用服务器发过来的K_S验证服务器发过来的签名后的H,如果验证一致,则说明此次秘钥交换成功。客户端向服务器发送new key,标志秘钥交换过程的结束。如果此次秘钥交换是整个会话的第一次交换,则计算出的H也是整个会话的会话ID(session_id)。

 

      秘钥基本信息在网络上的传输与交换,接下来就分别是服务器和客户端各自使用现有信息计算出以后加解密所要使用的秘钥。秘钥计算:

            这里的加密秘钥指的是以后数据通信所用的秘钥,一般用aes算法。

            计算方式:hash(K,H,单个字符,session_id);

            单个字符指的是单个大写的ASCII字母,根据不同的加密秘钥选择不同的字符来计算。

    

字母 秘钥
'A' 客户端到服务器的初始IV(CBC)
'B' 服务器到客户端的初始IV
'C' 客户端到服务器的加密秘钥(对称秘钥)
'D' 服务器到客户端的加密秘钥
'E' 客户端到服务器的完整性秘钥(HMAC)
'F' 服务器到客户端的完整性秘钥

            

 

        就以aes-cbc为例子,aes对称加解密所需要用到的值有初始IV与对称秘钥。这里的初始IV指的是cbc模式中加解密的初始向量,第二次加解密需要IV的值,以后的每次的加解密都要依赖于上一次加解密的数据。

 

    

    三、加密通信

       此时双方都拥有协商好的算法以及用于加解密的秘钥,现在开始所有传输的全部数据都要进行加密(包含总长度),并使用同样的。

        在加密通信的过程中,双方允许重新发送KEX秘钥交换请求。这时整个秘钥交换过程的数据将会使用现有密钥加解密。在该次秘钥交换的过程中也会生成一个H值,但该H值不会影响到此次会话的session_id,session_id只是会话第一次秘钥交换生成的H值。在秘钥交换最后客户端发出new keys请求时。双方会放弃当前使用的秘钥,使用新协商的秘钥继续通信。

        在远程数据的通信过程中,双方使用SSH_MSG_CHANNEL_DATA标志消息类型进行数据传输。

        在秘钥交换完成后第一次对发送数据加密时,首先需要对AES向量进行初始化,即设置对应的IV。aes部分我使用的是CRYPTOPP的aes-cbc算法(在后文的有对该算法的封装)。

    

1
2
3
4
5
6
7
         en_c_to_s.set_iv(dhdata.comp_encry_key(IVCSF,32));
     en_c_to_s.set_k(dhdata.comp_encry_key(ECSF,32));
     en_c_to_s.init();
 
     de_s_to_c.set_iv(dhdata.comp_encry_key(IVSCF,32));
     de_s_to_c.set_k(dhdata.comp_encry_key(ESCF,32));
     de_s_to_c.init();

    

    整个协议用到的主要加密算法的实现与封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//mycrypt.h
 
#ifndef _MYCRYPT_H__
#define _MYCRYPT_H__
 
#include<cstring>
#include<string>
#include <iostream>
#include<cmath>
#include "integer.h"
#include "files.h"
#include "hex.h"
#include "sha.h"
#include "modes.h"
#include "osrng.h"
 
using  namespace  std;
using  namespace  CryptoPP;
 
 
 
 
Integer mkrandomnum( int  len);
string inttostr(Integer num,unsigned  int  radix);
string inttostrnum(Integer num,unsigned  int  radix);
//大整数转mpint
string inttompint(Integer num,unsigned  int  radix);
string strtostrnum(string s);
//大整数快速幂运算
Integer fastpower_comp(Integer a,Integer b,Integer c);
 
//sha算法封装
class  m_sha
{
public :
     string encode_sha1(string data);
     string encode_sha256(string data);
};
//dh算法实现
class  m_dh
{
public :
     Integer dh_g,dh_p,dh_x,dh_e;
     Integer dh_y,dh_f;
     Integer dh_k;
 
     void  set_g_and_p( const  Integer g, const  Integer p)
     {
         dh_g=g;
         dh_p=p;
     }
     void  set_y(Integer y)
     {
         dh_y=y;
     }
     void  set_f(Integer f)
     {
         dh_f=f;
     }
     void  comp_e();
     Integer get_e()
     {
         return  dh_e;
     }
     void  comp_k();
     Integer get_k()
     {
         return  dh_k;
     }
};
//rsa算法实现
class  m_rsa
{
public :
     Integer rsa_e;
     Integer rsa_n;
     void  set_e_and_n(Integer e,Integer n)
     {
         rsa_e=e;
         rsa_n=n;
     }
     Integer comp_rsa_result(Integer num);
};
//dh gex协议算法实现
class  m_dh_gex_sha: public  m_dh, public  m_sha, public  m_rsa
{
public :
     string v_c,v_s;
     string i_c,i_s;
     string k_s;
     string dh_h,s_h;
 
     string shka_name;
 
     void  set_v_c(string x)
     {
         v_c=x;
     }
     void  set_v_s(string x)
     {
         v_s=x;
     }
     void  set_i_c(string x)
     {
         i_c=x;
     }
     void  set_i_s(string x)
     {
         i_s=x;
     }
     void  set_k_s(string x)
     {
         k_s=x;
     }
     void  set_s_h(string x)
     {
         s_h=x;
     }
     void  set_shka_name(string x)
     {
         shka_name=x;
     }
     void  comp_h();
     string get_h()
     {
         return  dh_h;
     }
     string comp_encry_key( char  c, const  int  len);
};
//aes-cbc算法封装
class  m_aes_cbc
{
public :
     string aes_k;
     string aes_iv;
     void  set_k(string x)
     {
         aes_k=x;
     }
     void  set_iv(string x)
     {
         aes_iv=x;
     }
 
     CBC_Mode<AES>::Encryption *aes_Encryptor;
     CBC_Mode<AES>::Decryption aes_Decryptor;
     void  init();
     string encode(string data);
     string decode(string data);
 
};
 
 
#endif //_MYCRYPT_H__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
//mycrypt.cpp
 
#include "mycrypt.h"
 
Integer mkrandomnum( int  len)
{
     Integer re=0;
     for ( int  i=0;i<len;i++)
     {
         re*=10;
         re+= abs ( rand ())%10;
     }
     return  re;
}
 
string inttostr(Integer num,unsigned  int  radix)
{
     string s1= "" ;
     unsigned  int  k;
     while (num>0)
     {
         k=num%radix;
         num/=radix;
         //cout<<num<<" ";
         //printf("%d\n",k);
         s1+=( char )k;
     }
     string s2= "" ;
     for ( int  i=s1.length()-1;i>=0;i--)
     {
         s2+=s1[i];
     }
     return  s2;
}
 
string inttostrnum(Integer num,unsigned  int  radix)
{
     string s1= "" ;
     unsigned  int  k;
     while (num>0)
     {
         k=num%radix;
         num/=radix;
         if (k<10)
         {
             s1+= '0' +k;
         }
         else
         {
             s1+= 'a' +k-10;
         }
     }
     string s2= "" ;
     for ( int  i=s1.length()-1;i>=0;i--)
     {
         s2+=s1[i];
     }
     return  s2;
}
 
string inttompint(Integer num,unsigned  int  radix)
{
     string s= "" ;
     s+=( char )0;
     s+=inttostr(num,radix);
     int  len=s.length();
     string k= "" ;
     while (len>0)
     {
         k+=( char )(len%256);
         len/=256;
     }
     while (k.length()<4)
     {
         k+=( char )0;
     }
     string re;
     re+=k[3];
     re+=k[2];
     re+=k[1];
     re+=k[0];
     re+=s;
 
     return  re;
}
 
string strtostrnum(string s)
{
     string re= "" ;
     int  k,p;
     for ( int  i=0;i<s.length();i++)
     {
         p=s[i];
         if (p<0)
         {
             p=(256+s[i]);
         }
         k=p/16;
         for ( int  j=0;j<2;j++)
         {
             //
             if (k<10)
             {
                 re+=( '0' +k);
             }
             else
             {
                 re+=( 'a' +k-10);
             }
             k=p%16;
         }
     }
     return  re;
}
 
int  intlength(Integer num)
{
     int  re=0;
     while (num>0)
     {
         num/=10;
         re++;
     }
     return  re;
}
 
 
Integer fastpower_comp(Integer a,Integer b,Integer c)
{
     
     /*unused fast power
     Integer re=1;
     for(int i=0;i<b;i++)
     {
         re*=a;
         re%=c;
     }
     return re;
     */
 
     //fast power
     Integer n=c;
     c=1;
     while (b!=0)
     {
         if (b%2!=0)
         {
             b=b-1;
             c=(c*a)%n;
         }
         else
         {
             b=b/2;
             a=(a*a)%n;
         }
     }
     return  c;
}
 
void  m_dh::comp_e()
{
     
     dh_x=mkrandomnum(50)+1;
     dh_e=fastpower_comp(dh_g,dh_x,dh_p);
 
     /*
     cout<<"//"<<endl;
     cout<<dh_g<<endl;
     cout<<dh_x<<endl;
     cout<<dh_p<<endl;
     cout<<dh_e<<endl;
     cout<<"//"<<endl;
     */
}
void  m_dh::comp_k()
{
     dh_k=fastpower_comp(dh_f,dh_x,dh_p);
}
 
Integer m_rsa::comp_rsa_result(Integer num)
{
     return  fastpower_comp(num,rsa_e,rsa_n);
}
 
 
void  m_dh_gex_sha::comp_h()
{
     string data= "" ;
     /*
     data+=strtostrnum(v_c);
     data+=strtostrnum(v_s);
     data+=strtostrnum(i_c);
     data+=strtostrnum(i_s);
     data+=strtostrnum(k_s);
     data+=inttostrnum(dh_e,16);
     data+=inttostrnum(dh_f,16);
     data+=inttostrnum(dh_k,16);
     */    
     data+=v_c;
     data+=v_s;
     data+=i_c;
     data+=i_s;
     data+=k_s;
     data+=inttompint(dh_e,16);
     data+=inttompint(dh_f,16);
     data+=inttompint(dh_k,16);
     
 
     //cout<<endl<<"|"<<data<<"||"<<endl;
 
     dh_h=encode_sha256(data);
}
 
string m_dh_gex_sha::comp_encry_key( char  c, const  int  len)
{
     string re;
 
     string data= "" ;
     data+=inttompint(dh_k,16);
     data+=dh_h;
     data+=c;
     data+=dh_h;
     re=encode_sha256(data);
 
     while (re.length()<len)
     {
         data=inttompint(dh_k,16);
         data+=dh_h;
         data+=re;
         re+=encode_sha256(data);
     }
     while (re.length()>len)
     {
         re.pop_back();
     }
     return  re;
}
 
 
string m_sha::encode_sha1(string data)
{
     string hash; 
     SHA1 sha1; 
     HashFilter hash_filter (sha1); 
 
     hash_filter.Attach( new  HexEncoder( new  StringSink(hash),  false )); 
     hash_filter.Put((byte *)data.c_str(),data.length());
     hash_filter.MessageEnd(); 
 
     return  hash; 
}
 
string m_sha::encode_sha256(string data)
{
     string hash; 
     SHA256 sha256; 
     HashFilter hash_filter (sha256); 
 
     hash_filter.Attach( new  HexEncoder( new  StringSink(hash),  false )); 
     hash_filter.Put((byte *)data.c_str(),data.length());
     hash_filter.MessageEnd(); 
 
     return  hash; 
}
 
void  m_aes_cbc::init()
{
 
     aes_Encryptor= new  CBC_Mode<AES>::Encryption((unsigned  char  *)aes_k.c_str(),
         aes_k.length(),
         (unsigned  char  *)aes_iv.c_str());
 
     aes_Decryptor= new  CBC_Mode<AES>::Decryption ((unsigned  char  *)aes_k.c_str(),
         aes_k.length(),
         (unsigned  char  *)aes_iv.c_str());
}
 
string m_aes_cbc::encode(string data)
{
     string re;
     StringSource(data,
         true ,
         new  StreamTransformationFilter(*aes_Encryptor,
         new  StringSink(re),
         BlockPaddingSchemeDef::BlockPaddingScheme::ONE_AND_ZEROS_PADDING,
         true )
         );
     return  re;
}
 
string m_aes_cbc::decode(string data)
{
     string re;
     StringSource(data, 
         true ,
         new  StreamTransformationFilter(*aes_Decryptor,
         new  StringSink(re),
         BlockPaddingSchemeDef::BlockPaddingScheme::ONE_AND_ZEROS_PADDING,
         true )
         );
     return  re;
}

    ssh的实现就到此终于结束了,截图留念。

    

    

    笔者在之初就想使用crypto++来帮助实现ssh过程的密码算法。而刚接触这东西完全搞不懂怎么用,什么编码器、生成器、过滤器、sink...这些概念根本就不懂,网上的使用文档直接就拿这一堆概念加上一堆组合出来的代码来实现一个加密算,没有什么密码学知识,想要快速掌握crypto++几乎是不可能的,当时研究了很久就只是会使用它的hash加密。而后自己硬着头皮实现了整个dh-gex,到后面aes后,发现自己能很自然得理解crypto++的用法了,便自己封装了crypto++的aes算法供使用。

    总之都是好事,以后遇到其他的基于ssl的协议与应用就应能很轻松地理解与实现了。

转发地址:http://www.cnblogs.com/wchrt/p/4550208.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值