課程:Java程序設計 班級:1652班 姓名:朱文遠 學號:20165214html
指導教師:婁嘉鵬 實驗日期:2018年5月28日java
實驗時間:15:35 - 17:15 實驗序號:五git
實驗名稱: 網絡編程與安全算法
實驗目的:
一、掌握Java Socket的相關內容;
二、學會創建客戶端與服務器端之間的聯繫;
三、學習並應用密碼學的相關內容編程
一、基於Java Socket實現客戶端/服務器功能,傳輸方式用TCP數組
二、客戶端讓用戶輸入中綴表達式,而後把中綴表達式調用MyBC.java的功能轉化爲後綴表達式,把後綴表達式用3DES或AES算法加密經過網絡把密文和明文的MD5値發送給服務器安全
三、客戶端和服務器用DH算法進行3DES或AES算法的密鑰交換服務器
四、服務器接收到後綴表達式表達式後,進行解密,解密後計算明文的MD5值,和客戶端傳來的MD5進行比較,一致則調用MyDC.java的功能計算後綴表達式的值,把結果發送給客戶端網絡
五、客戶端顯示服務器發送過來的結果多線程
最後將每一個任務的代碼上傳到碼雲。
任務(一)早在結隊編程—四則運算中咱們就已經實現了中綴轉後綴的功能,以及將後綴表達式利用棧進行運算的功能。所以任務(一)咱們直接在以前代碼的基礎上進行修改便可。
代碼運行結果以下,能夠多種符號的四則運算。
套接字:IP地址表示計算機,端口號表示進程(線程),Socket類建立套接字對象並鏈接在一塊兒(端口號與IP地址組合)。
客戶端程序用Socket類建立負責鏈接到服務器的套接字對象,其構造方法爲Socket([IP地址],[端口號])(可能拋出IOException異常)。對套接字對象創建後,可使用①getInputStream()得到一個輸入流來讀取服務器寫入到輸出流中的數據;②getOutputStream()得到一個輸出流,服務器能夠用輸入流來讀取客戶寫入到輸出流中的數據。
客戶負責創建鏈接到服務器的套接字對象。服務器須要建立一個ServerSocket對象來將客戶端的套接字對象與服務器的套接字對象鏈接起來。ServerSocket的構造方法是ServerSocket([端口號])(當端口已被佔用會拋出IOException異常)。接着,ServerSocket對象調用accept()方法再次返回一個與客戶端對象相鏈接的新的Socket對象。一樣的,它也具備上述的兩個方法。
從套接字鏈接中讀取數據,可能在另外一端數據發送以前就已經開始讀取了,並且會阻塞本線程,直到成功讀取到信息。同時,accept方法也會阻塞線程的執行,直到收到客戶的呼叫。爲了解決「收不到呼叫而致使程序沒法繼續運行」的狀況,ServerSocket對象在調用accept方法以前能夠先調用setTimeout(s)方法來使得在調用accept方法時若是超過s毫秒沒有收到呼叫,就拋出SocketTimeoutException異常。
雙方通訊完畢,套接字要使用close()方法關閉套接字鏈接。
使用多線程技術:因爲使用套接字鏈接中讀取數據時,可能會阻塞本線程直到成功讀取到信息。爲了不這種狀況,須要啓動一個專門爲該客戶服務的線程。Socket的構造方法Socket()能夠建立一個套接字對象,該對象調用public void connect(SocketAddress endpoint) throws IOException
來與指定的套接字建立鏈接。這裏的參數可使用InetSocketAddress的構造方法public InetSocketAdress(InetAdress addr,int port)
來得到。套接字通訊的兩個基本原則:
①服務器要啓動一個專門的線程與客戶的套接字創建鏈接;
②套接字的輸入流在讀取信息時可能發生阻塞,因此客戶端與服務器端都須要在一個單獨的線程中讀取信息。
-- 引用自個人第九周學習任務
System.out.print("輸入中綴表達式:"); while(scanner.hasNext()) { String middle="";//初始化middle try { middle = scanner.next();//輸入中綴表達式 } catch(InputMismatchException exp){ System.exit(0); } try { myBC.setNormal(middle); myBC.change();//調用M有BC中的類來將中綴表達式轉化爲後綴表達式。 System.out.println("後綴表達式:"+myBC.Behind); //Behind是類變量,因此能夠經過類名來調用。 out.writeUTF(myBC.Behind);//將後綴表達式寫進流中 } catch(Exception e) {} }
... public void run() { String result=""; while(true) { try{ result=in.readUTF();//讀取從服務器端傳輸回來的結果 System.out.println("結果:"+result); System.out.println("輸入中綴表達式:");//能夠再次輸入新的表達式進行運算。 } catch(IOException e) { System.out.println("與服務器已斷開"+e); break; } } } ...
... @Override public void run() { while(true) { try{ String r=in.readUTF();//堵塞狀態,除非讀取到信息 String result=caculate.caculate(r);//調用Caculate類進行運算 out.writeUTF(result);//將結果寫入到流中 } catch (IOException e) { System.out.println("客戶離開"); return; } } } ...
服務器端截圖
... String cipher=sEnc.Cipher(myBC.Behind);//將後綴表達式加密 out.writeUTF(cipher);//將加密結果寫入到流中讓服務器端讀取 ...
... String r=in.readUTF();//讀取客戶端傳過來的密文 String plain=sDec.Plain(r);//解密 String result=caculate.caculate(plain);//將解密獲得的後綴表達式進行計算,而後返回計算結果。 ...
Diffie-Hellman是一種創建密鑰的方法,而不是加密方法。然而,它所產生的密鑰可用於加密、進一步的密鑰管理或任何其它的加密方式。
基於原根的定義及性質,能夠定義Diffie-Hellman密鑰交換算法.該算法描述以下:
1,有兩個全局公開的參數,一個素數q和一個整數a,a是q的一個原根.
2,假設用戶A和B但願交換一個密鑰,用戶A選擇一個做爲私有密鑰的隨機數XA(XA<q),並計算公開密鑰YA=a^XA mod q。A對XA的值保密存放而使YA能被B公開得到。相似地,用戶B選擇一個私有的隨機數XB<q,並計算公開密鑰YB=a^XB mod q。B對XB的值保密存放而使YB能被A公開得到.
3,用戶A產生共享祕密密鑰的計算方式是K = (YB)^XA mod q.一樣,用戶B產生共享祕密密鑰的計算是K = (YA)^XB mod q.這兩個計算產生相同的結果: K = (YB)^XA mod q = (a^XB mod q)^XA mod q = (aXB)XA mod q (根據取模運算規則獲得) = a^(XBXA) mod q = (aXA)XB mod q = (a^XA mod q)^XB mod q = (YA)^XB mod q 所以至關於雙方已經交換了一個相同的祕密密鑰.
4,由於XA和XB是保密的,一個敵對方能夠利用的參數只有q,a,YA和YB.於是敵對方被迫取離散對數來肯定密鑰.例如,要獲取用戶B的祕密密鑰,敵對方必須先計算 XB = inda,q(YB) 而後再使用用戶B採用的一樣方法計算其祕密密鑰K. Diffie-Hellman密鑰交換算法的安全性依賴於這樣一個事實:雖然計算以一個素數爲模的指數相對容易,但計算離散對數卻很困難.對於大的素數,計算出離散對數幾乎是不可能的. 下面給出例子.密鑰交換基於素數q = 97和97的一個原根a = 5.A和B分別選擇私有密鑰XA = 36和XB = 58.每人計算其公開密鑰 YA = 5^36 = 50 mod 97 YB = 5^58 = 44 mod 97 在他們相互獲取了公開密鑰以後,各自經過計算獲得雙方共享的祕密密鑰以下: K = (YB)^XA mod 97 = 44^36 = 75 mod 97 K = (YA)^XB mod 97 = 50^58 = 75 mod 97 從|50,44|出發,攻擊者要計算出75很不容易.
-- 引用自百度百科
... public String MD5(String cipher) throws Exception{ MessageDigest m=MessageDigest.getInstance("MD5"); m.update(cipher.getBytes("UTF8")); byte s[ ]=m.digest( ); String result=""; for (int i=0; i<s.length; i++){ result+=Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6);//將加密結果轉化爲16進制字符串 } System.out.println("MD5加密後的結果:"+result);//輸出加密後的結果 return result;//返回加密後的結果。 ...
... String r=in.readUTF();//讀取信息到r中(r是密文) String plain=sDec.Plain(r);//將r解密獲得明文 String message=digestPass.MD5(plain);//將解密後獲得的明文用MD5算法加密,放入message中 String result=caculate.caculate(plain);//將解密後獲得的算式進行運算 String q=in.readUTF();//獲得用戶端傳送過來用MD5加密的明文q。 if(message.equals(q)){ //比較message和q,若是相等則校驗成功,將計算結果返回。 System.out.println("校驗成功!"); out.writeUTF(result); ...
... String cipher=sEnc.Cipher(myBC.Behind);//將後綴表達式使用DES算法加密,獲得密文cipher out.writeUTF(cipher);把密文cipher寫入流中以讓服務器端讀取。 out.writeUTF(digestPass.MD5(myBC.Behind));//將後綴表達式(明文)使用MD5算法加密,而後寫入流中以讓服務器端讀取。 ...
問題1:在調試代碼的時候,有時候運行服務器端的程序會出現這樣的問題:
問題1解決:緣由是我沒有將以前的服務器端口關閉,而是開了多個服務器端口,卻使用了一樣的端口,因此致使了這個狀況。把以前的端口先關掉,問題就解決了。
本次實驗讓我對網絡編程與安全有了初步的瞭解,同時對於Java的密碼學應用相比以前有了更深入的認識。此次實驗讓我在Java、計算機網絡、密碼學三個方面有所學習有所進步,能夠說是一舉三得。所以我以爲這是一次頗有意義的實驗。