SSH2.0编程 ssh协议过程实现(转)
?
SSh協(xié)議:
全稱為Secure Shell,即很安全的shell,主要目的是用來(lái)取代傳統(tǒng)的telnet和r系列命令(rlogin,rsh,rexec等)遠(yuǎn)程登錄和遠(yuǎn)程執(zhí)行命令的工具,實(shí)現(xiàn)遠(yuǎn)程登錄和遠(yuǎn)程執(zhí)行命令加密,防止由于網(wǎng)絡(luò)監(jiān)聽(tīng)而出現(xiàn)的密碼泄露,從而對(duì)系統(tǒng)構(gòu)成威脅。(telnet協(xié)議采用明文傳送密碼,數(shù)據(jù)傳送過(guò)程中也不加密)
ssh協(xié)議目前有ssh1 和ssh2,其實(shí)現(xiàn)在我們主要使用的也是openssh。ssh不僅在登錄過(guò)程中對(duì)密碼進(jìn)行加密傳送,而且在登錄后執(zhí)行的命令的數(shù)據(jù)也進(jìn)行加密,這樣即使別人在網(wǎng)絡(luò)上監(jiān)聽(tīng)并截獲了你的數(shù)據(jù)包,他也看不到其中的內(nèi)容。
?
? 之前為了自己做一套SSH,先自己實(shí)現(xiàn)了一套telnet。但經(jīng)過(guò)這么多天的苦逼,發(fā)現(xiàn)以前的工作都是徒勞。ssh的協(xié)議很繁雜,核心的內(nèi)容在于密碼算法,而且自己很難在網(wǎng)上找到周全的細(xì)節(jié)講解與詳細(xì)的實(shí)現(xiàn),只有靠自己刷RFC和問(wèn)大神還有就是靠強(qiáng)X我的服務(wù)器艱難地完成。
??? 現(xiàn)計(jì)算了下時(shí)間,自己做SSH耗費(fèi)了進(jìn)兩個(gè)月的時(shí)間,雖然期間也夾著其他的繁雜事物,但自己在這方面確是是耗費(fèi)了非常大的精力。因?yàn)檫@方面詳細(xì)資料的匱乏,自己以前也幾乎沒(méi)有接觸過(guò)密碼學(xué)方面的東西,很多只有靠自己摸索,所以我得經(jīng)常拿我自己的服務(wù)器來(lái)做黑盒測(cè)試,我現(xiàn)在服務(wù)器上的ss服務(wù)器日志全是一些非法連接的記錄(—_—|||)。早知當(dāng)初就不那么作死非要自己實(shí)現(xiàn)他的加密算法和過(guò)程,用openssl就很快搞定了。但我還是覺(jué)得這次做SSH的精力是我受益匪淺,不僅熟悉了各種加密,并且能靠自己實(shí)現(xiàn)并熟練應(yīng)用了。可能這些對(duì)自己幫助不大,但至少和信安的小伙伴也有點(diǎn)吹牛的談資了~
??? 這篇文章希望能幫助到想了解ssh2.0協(xié)議或是親手實(shí)現(xiàn)ssh協(xié)議的小伙伴。
?
??? 首先對(duì)數(shù)據(jù)包的格式進(jìn)行說(shuō)明:
??? 數(shù)據(jù)包由包長(zhǎng)度(Packet Length)、填充長(zhǎng)讀(Padding Length)、信息代碼(Msg code)、信息內(nèi)容與填充值(Padding String) 這5部分組成。信息內(nèi)容中的一些字符串以4字節(jié)長(zhǎng)度+該長(zhǎng)度數(shù)量的字符組成,數(shù)值按照網(wǎng)絡(luò)序排列,例如:abc: 00 00 00 03 (char)a (char)b (char)c 。另外有一種大整數(shù)的情況,負(fù)數(shù)和字符串的表示方式一樣,正數(shù)需要前導(dǎo)0,例如 4b64: 00 00 00 03 00 4b 64 。
??? ssh頭的結(jié)構(gòu)體:
| 1 2 3 4 5 6 7 | struct sshhead { ??? unsigned int tlen; ??? unsigned char plen; ??? unsigned char msgcode; ??? sshhead(){tlen=6;} }; |
?
??? 就拿通過(guò)ssh遠(yuǎn)程控制的一個(gè)完整個(gè)過(guò)程來(lái)講,ssh的過(guò)程可分為以下3部分:
??? 一、版本協(xié)商
??? 二、算法協(xié)商與密鑰交換
??? 三、加密通信(可能含有2、3部分)
??? 這其中第二部分是ssh最為核心的過(guò)程,該過(guò)程決定了以后通信所要使用的密鑰,下面按順序?qū)γ總€(gè)部分對(duì)比著數(shù)據(jù)包進(jìn)行詳細(xì)的講解并給出實(shí)現(xiàn)的過(guò)程。
???
??? 一、版本協(xié)商:
??? 在建立連接后,客戶端與服務(wù)器分別向?qū)Ψ桨l(fā)送自己ssh的版本信息(這里的數(shù)據(jù)格式不同于其他包,只有一行版本號(hào)),以\r\n結(jié)束。版本的格式如下:
???????????????????????? SSH-ssh協(xié)議版本-詳細(xì)版本\r\n (幾乎只有ssh協(xié)議版本之前的信息有效)
??? 比如我linux上的就是:SSH-2.0-OpenSSH_5.3\r\n
??? Putty的是:????????? SSH-2.0-PuTTY_Release_0.63\r\n
???
??? 一般來(lái)說(shuō),在建立連接后,是先由服務(wù)器發(fā)版本號(hào)過(guò)來(lái),單線程處理版本協(xié)商的朋友需要注意下。
??? 在雙方收到對(duì)方發(fā)來(lái)的版本號(hào)后,會(huì)根據(jù)兩者之中最小的版本來(lái)進(jìn)行接下來(lái)的通訊。
???
??? 二、算法協(xié)商與秘鑰交換:
??? 這部分的內(nèi)容將會(huì)占該文章總篇幅的一半以上。
??? 首先給大家看下整個(gè)過(guò)程的數(shù)據(jù)包大概:
??? ??? 整個(gè)部分是從第6條開(kāi)始到第15條結(jié)束,除去中間的非協(xié)議部分,總共有7條數(shù)據(jù)包。看起來(lái)只有這么幾條數(shù)據(jù)包,但其中包含了非常多的過(guò)程與隱秘的信息。
??? 1、算法協(xié)商:
??????? 位第6、9數(shù)據(jù),分別為雙發(fā)向?qū)Ψòl(fā)送的自己在不同密碼需求上支持的算法。
???? 該數(shù)據(jù)包的格式:
??? 按順序分別是:
??????? cookie(隨機(jī)的值,16byte)
??????? kex_algorithms(秘鑰租交換算法)
??????? server_host_key_algorithms(服務(wù)器主機(jī)秘鑰,正常情況用處不大,甚至可以不用)
??????? encryption_algorithms_client_to_server(兩端通信使用的加密算法)
??????? encryption_algorithms_server_to_client
??????? mac_algorithms_client_to_server(數(shù)據(jù)校驗(yàn)用的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整數(shù),擴(kuò)展用的)
??? 每個(gè)算法類型可能會(huì)有多個(gè)不同的算法,這些算法之間使用逗號(hào)隔開(kāi)。
??? 現(xiàn)在雙方知道對(duì)方支持的算法,但是應(yīng)該怎樣決定每個(gè)類型實(shí)際所使用的算法呢?
??? 每個(gè)算法類型列表的第一個(gè)算法必須是首選的算法,服務(wù)器應(yīng)以客戶端的算法優(yōu)先級(jí)作為考慮,就拿交換算法舉例:
??????? 現(xiàn)在服務(wù)器有三個(gè)算法dh1,dh2,dh3
??????????? 客戶端有兩個(gè)算法dh3,dh2
??????? 那么服務(wù)器的首選算法是dh1,而客戶端是dh3,客戶端此時(shí)知道服務(wù)器有dh3算法,因此客戶端就確認(rèn)使用dh3算法。服務(wù)器發(fā)現(xiàn)自己的首選算法與客戶端不同,而自己擁有客戶端的首選算法,因此服務(wù)器也確認(rèn)使用dh3算法。
??????? 再看另一個(gè)情況
??????????? 服務(wù)器:dh1,dh2,dh3
??????????? 客戶端:dh4,dh3,dh1
??????? 這時(shí)服務(wù)器沒(méi)有客戶端的首選算法,客戶端會(huì)使用第二個(gè)算法dh3,此時(shí)服務(wù)器也支持第二個(gè)算法,雙方將確定使用dh3算法。
??? 如果服務(wù)器和客戶端雙方?jīng)]有共同的算法,這次會(huì)話將會(huì)終止。
??? 下面是代碼實(shí)現(xiàn)和服務(wù)器之間的版本協(xié)商
| 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; ??? //將算法列表與信息分別寫(xiě)入緩沖區(qū) ??? 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; ??? } //載荷的計(jì)算與總長(zhǎng)度的寫(xiě)入都放在最后 ??? //count padding length ??? count_padding(sshh); ??? sshheadin(sshh); ??? //沒(méi)有封裝socket ??? len=send(sock,data,sshh.tlen+4,0); ??? mrecv(10); ??? //printf("(%d)",len); ??? if (data[5]==KEI) ??? { ??????? return true ; ??? } ??? return false ; } |
用到的一些功能函數(shù):
| 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 | //向緩沖區(qū)填充字符串,長(zhǎng)度使用網(wǎng)絡(luò)字節(jié)序 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、秘鑰交換
??????? 在算法協(xié)商成功過(guò)后,雙方便立馬進(jìn)行秘鑰組的交換。ssh2.0版本所使用的秘鑰組交換協(xié)議算法主要使用diffie-hellman-group-exchange-sha算法。
???????? 鑒于該部分內(nèi)容特別多,我特意在另一篇單獨(dú)的文章中予以詳細(xì)介紹,再閱讀下文前請(qǐng)先參考該文章:dh-gex-sha算法詳解
??????? 我們數(shù)據(jù)包的第10到15條都是該部分的內(nèi)容
???????
??? 1、dh key exchange init (C)
??????? 密鑰交換初始化,由客戶端先向服務(wù)器發(fā)送秘鑰交換請(qǐng)求的數(shù)據(jù)包,告知開(kāi)始秘鑰交換。
???????
| 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)
??????? 服務(wù)器收到客戶端發(fā)起交換的請(qǐng)求后,將自己用于dh算法的P、G發(fā)送給客戶端,用于客戶端生成dh公私鑰。這里的P是一個(gè)大素?cái)?shù),而G是大于1的數(shù),G不必過(guò)大,10位以內(nèi)最后,因?yàn)榘磧邕\(yùn)算G能輕易生成特別大的數(shù)。
???????
| 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)
??????? 客戶端收到服務(wù)器發(fā)過(guò)來(lái)的P、G后,自己變成根據(jù)P、G生成并計(jì)算出自己的公鑰e。這一步也只需要客戶端將生成的e發(fā)送給服務(wù)器即可。
| 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)
??????? 重要的來(lái)了,服務(wù)器收到了客戶端發(fā)來(lái)的e后,便能計(jì)算出共享秘鑰K,并根據(jù)現(xiàn)有信息計(jì)算出生成所需秘鑰的H。
??????? 這個(gè)數(shù)據(jù)包里面含有如下信息:
??????? KEX DH host key(K_S):
??????????? 主機(jī)公鑰,一般為rsa公鑰。完整的格式為:總長(zhǎng)度+算法名長(zhǎng)度+算法名+證書(shū)(n)長(zhǎng)度+證書(shū)(n)+公鑰長(zhǎng)度+公鑰。
??????? DH server f :
??????????? 服務(wù)器的dh公鑰值,客戶端收到后便能用f計(jì)算出同樣的共享秘鑰K。
??????? KEX DH H signature (簽名后的H):
??????????? 服務(wù)器用主機(jī)私鑰對(duì)計(jì)算出的hash值H進(jìn)行簽名的結(jié)果。格式為:總長(zhǎng)度+算法名長(zhǎng)度+算法名+簽名數(shù)據(jù)長(zhǎng)度+簽名值。
??????????? H的計(jì)算方法: H=hash(V_C||V_S||I_C||I_S||K_S||e||f||K);
???????????? 按順序用到的值(注意類型):
???
| 類型 | 值 | 說(shuō)明 |
| string | V_C | 客戶端的初始報(bào)文(版本信息:SSH-2.0-xxx,不含結(jié)尾的CR和LF) |
| string | V_S | 服務(wù)器的初始報(bào)文 |
| string | I_C | 客戶端 SSH_MSG_KEX_INIT的有效載荷(不含開(kāi)頭的數(shù)據(jù)長(zhǎng)度值) |
| string | I_S | 服務(wù)器的同上 |
| string | K_S | 主機(jī)秘鑰(dh gex reply(33)過(guò)程服務(wù)器發(fā)送host key (RSA公鑰)) |
| mpint | e | 客戶端DH公鑰 |
| mpint | f | 服務(wù)器DH公鑰 |
| mpint | K | 共同DH計(jì)算結(jié)果 |
???????
| 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)
????????
????????? 客戶端收到服務(wù)器的信息后計(jì)算出K,并用同樣的方式計(jì)算出H(服務(wù)器和客戶端的H都是同一個(gè)值)。并使用服務(wù)器發(fā)過(guò)來(lái)的K_S驗(yàn)證服務(wù)器發(fā)過(guò)來(lái)的簽名后的H,如果驗(yàn)證一致,則說(shuō)明此次秘鑰交換成功。客戶端向服務(wù)器發(fā)送new key,標(biāo)志秘鑰交換過(guò)程的結(jié)束。如果此次秘鑰交換是整個(gè)會(huì)話的第一次交換,則計(jì)算出的H也是整個(gè)會(huì)話的會(huì)話ID(session_id)。
?
????? 秘鑰基本信息在網(wǎng)絡(luò)上的傳輸與交換,接下來(lái)就分別是服務(wù)器和客戶端各自使用現(xiàn)有信息計(jì)算出以后加解密所要使用的秘鑰。秘鑰計(jì)算:
??????????? 這里的加密秘鑰指的是以后數(shù)據(jù)通信所用的秘鑰,一般用aes算法。
??????????? 計(jì)算方式:hash(K,H,單個(gè)字符,session_id);
??????????? 單個(gè)字符指的是單個(gè)大寫(xiě)的ASCII字母,根據(jù)不同的加密秘鑰選擇不同的字符來(lái)計(jì)算。
???
| 字母 | 秘鑰 |
| 'A' | 客戶端到服務(wù)器的初始IV(CBC) |
| 'B' | 服務(wù)器到客戶端的初始IV |
| 'C' | 客戶端到服務(wù)器的加密秘鑰(對(duì)稱秘鑰) |
| 'D' | 服務(wù)器到客戶端的加密秘鑰 |
| 'E' | 客戶端到服務(wù)器的完整性秘鑰(HMAC) |
| 'F' | 服務(wù)器到客戶端的完整性秘鑰 |
???????????
?
??????? 就以aes-cbc為例子,aes對(duì)稱加解密所需要用到的值有初始IV與對(duì)稱秘鑰。這里的初始IV指的是cbc模式中加解密的初始向量,第二次加解密需要IV的值,以后的每次的加解密都要依賴于上一次加解密的數(shù)據(jù)。
?
???
??? 三、加密通信
?????? 此時(shí)雙方都擁有協(xié)商好的算法以及用于加解密的秘鑰,現(xiàn)在開(kāi)始所有傳輸?shù)娜繑?shù)據(jù)都要進(jìn)行加密(包含總長(zhǎng)度),并使用同樣的。
??????? 在加密通信的過(guò)程中,雙方允許重新發(fā)送KEX秘鑰交換請(qǐng)求。這時(shí)整個(gè)秘鑰交換過(guò)程的數(shù)據(jù)將會(huì)使用現(xiàn)有密鑰加解密。在該次秘鑰交換的過(guò)程中也會(huì)生成一個(gè)H值,但該H值不會(huì)影響到此次會(huì)話的session_id,session_id只是會(huì)話第一次秘鑰交換生成的H值。在秘鑰交換最后客戶端發(fā)出new keys請(qǐng)求時(shí)。雙方會(huì)放棄當(dāng)前使用的秘鑰,使用新協(xié)商的秘鑰繼續(xù)通信。
??????? 在遠(yuǎn)程數(shù)據(jù)的通信過(guò)程中,雙方使用SSH_MSG_CHANNEL_DATA標(biāo)志消息類型進(jìn)行數(shù)據(jù)傳輸。
??????? 在秘鑰交換完成后第一次對(duì)發(fā)送數(shù)據(jù)加密時(shí),首先需要對(duì)AES向量進(jìn)行初始化,即設(shè)置對(duì)應(yīng)的IV。aes部分我使用的是CRYPTOPP的aes-cbc算法(在后文的有對(duì)該算法的封裝)。
???
| 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(); |
???
??? 整個(gè)協(xié)議用到的主要加密算法的實(shí)現(xiàn)與封裝:
| 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); //大整數(shù)轉(zhuǎn)mpint string inttompint(Integer num,unsigned int radix); string strtostrnum(string s); //大整數(shù)快速冪運(yùn)算 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算法實(shí)現(xiàn) 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算法實(shí)現(xiàn) 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協(xié)議算法實(shí)現(xiàn) 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的實(shí)現(xiàn)就到此終于結(jié)束了,截圖留念。
???
???
??? 筆者在之初就想使用crypto++來(lái)幫助實(shí)現(xiàn)ssh過(guò)程的密碼算法。而剛接觸這東西完全搞不懂怎么用,什么編碼器、生成器、過(guò)濾器、sink...這些概念根本就不懂,網(wǎng)上的使用文檔直接就拿這一堆概念加上一堆組合出來(lái)的代碼來(lái)實(shí)現(xiàn)一個(gè)加密算,沒(méi)有什么密碼學(xué)知識(shí),想要快速掌握crypto++幾乎是不可能的,當(dāng)時(shí)研究了很久就只是會(huì)使用它的hash加密。而后自己硬著頭皮實(shí)現(xiàn)了整個(gè)dh-gex,到后面aes后,發(fā)現(xiàn)自己能很自然得理解crypto++的用法了,便自己封裝了crypto++的aes算法供使用。
??? 總之都是好事,以后遇到其他的基于ssl的協(xié)議與應(yīng)用就應(yīng)能很輕松地理解與實(shí)現(xiàn)了。
http://www.cnblogs.com/wchrt/p/4550208.html
?
總結(jié)
以上是生活随笔為你收集整理的SSH2.0编程 ssh协议过程实现(转)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: perf trace跟踪系统调用
- 下一篇: 2020 年 1 月 14 日外延支持结