SSH2.0編程 ssh協議過程實現(轉)


 

  SSh協議:

  全稱為Secure Shell,即很安全的shell,主要目的是用來取代傳統的telnet和r系列命令(rlogin,rsh,rexec等)遠程登錄和遠程執行命令的工具,實現遠程登錄和遠程執行命令加密,防止由於網絡監聽而出現的密碼泄露,從而對系統構成威脅。(telnet協議采用明文傳送密碼,數據傳送過程中也不加密)

  ssh協議目前有ssh1 和ssh2,其實現在我們主要使用的也是openssh。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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM