2018-2019-2 20175306實驗五《網絡編程與安全》實驗報告

2018-2019-2 20175306實驗五《網絡編程與安全》實驗報告


實驗步驟

(一)網絡編程與安全-1

實驗要求:

兩人一組結對編程:java

  1. 參考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA
  2. 結對實現中綴表達式轉後綴表達式的功能 MyBC.java
  3. 結對實現從上面功能中獲取的表達式中實現後綴表達式求值的功能,調用MyDC.java
  4. 上傳測試代碼運行結果截圖和碼雲連接git

    知識點:

    1.棧的一個應用是用來對四則運算表達式進行求值。算法

表達式Exp = S1 + OP + S2(S1 ,S2是兩個操做數,OP爲運算符)有三種標識方法:編程

  • OP + S1 + S2 爲前綴表示法數組

  • S1 + OP + S2 爲中綴表示法安全

  • S1 + S2 + OP 爲後綴表示法服務器

前綴式: + * a b * - c / d e f網絡

中綴式: a * b + c - d / e * fsocket

後綴式: a b * c d e / - f * +
能夠看出:
- 操做數之間的相對次序不變; - 運算符的相對次序不一樣; - 中綴式丟失了括弧信息,導致運算次序不肯定; - 前綴式的運算規則爲:連續出現的兩個操做數和在它們以前且緊靠它們的運算符構成一個最小表達式; - 後綴式的運算規則爲:運算符在式中出現的順序恰爲表達式的運算順序;每一個運算符和在它以前出現且緊靠它的兩個操做數構成一個最小表達式。
後綴表示法是波蘭邏輯學家J.Lukasiewicz於1929年提出的,又叫作逆波蘭表達式。
Linux命令dc能夠用來對逆波蘭式表達式進行求值,dc的打印類命令:p:打印棧頂元素並換行

n: 打印棧頂元素並將其彈出棧,完畢後不換行

P: putchar ( int(棧頂元素) % 256) 並彈棧頂,不換行

f: 從棧頂至棧底打印棧中全部值,每一個一行

dc的運算符:

+: 依次彈出w1與w2,將w2+w1壓棧。精度爲結果值精度

-: 依次彈出w1與w2,將w2-w1壓棧

*: 依次彈出w1與w2,將w2*w1壓棧。精度爲結果值精度與precision中較大值

/ : 依次彈出w1與w2,將w2/w1壓棧。精度爲precision

%: 依次彈出w1與w2,將w2-w2/w1*w1壓棧

~: 依次彈出w1與w2,依次將w2/w1與w2%w1壓棧

^ : 依次彈出w1與w2,將w2^((int)w1)壓棧。精度爲w2精度與precision中較大值

| : 依次彈出w1 w2與w3,將 w3 ^ ((int)w2) (mod w1) 壓棧。w1 w3 需爲整數

v: 彈出w1,將sqrt(v)壓棧。精度爲precision

dc支持棧操做:

c : 清空棧

d : 將棧頂元素複製並壓棧

r : 交換棧頂兩元素 XXX
咱們如何實現dc? 這要用到棧。對逆波蘭式求值時,不須要再考慮運算符的優先級,只需從左到右掃描一遍後綴表達式便可。求值僞代碼以下:
- 設置一個操做數棧,開始棧爲空; - 從左到右掃描後綴表達式,遇操做數,進棧; - 若遇運算符,則從棧中退出兩個元素,先退出的放到運算符的右邊,後退出的放到運算符左邊,運算後的結果再進棧,直到後綴表達式掃描完畢。此時,棧中僅有一個元素,即爲運算的結果。
Linux中另一個計算器bc就是用來計算中綴表達式的:

如何由中綴式求得後綴式?

由中綴式求得後綴式可使用棧,僞代碼以下:
- 設立一個棧,存放運算符,首先棧爲空; - 從左到右掃描中綴式,若遇到操做數,直接輸出,並輸出一個空格做爲兩個操做數的分隔符; - 若遇到運算符,則與棧頂比較,比棧頂級別高則進棧,不然退出棧頂元素並輸出,而後輸出一個空格做分隔符; - 若遇到左括號,進棧;若遇到右括號,則一直退棧輸出,直到退到左括號止。 - 當棧變成空時,輸出的結果即爲後綴表達式。

實驗代碼:

  • 中綴轉後綴:
import java.util.Stack;

public class MyBC {
    MyBC(){}
    public static String infixToSuffix(String exp){
        Stack<String> s = new Stack<String>();         // 建立操做符堆棧
        String suffix = "";            // 要輸出的後綴表達式字符串
        String suffix1 = "";             //上一次的後綴表達式
        String suffix2 = "";
        String str[] = exp.split(" ");
        int length = str.length; // 輸入的中綴表達式的長度
        String temp="";
        for (int i = 0; i < length; i++) {            // 對該中綴表達式的每個字符並進行判斷
            switch (str[i]) {
                case " ":break;           // 忽略空格
                case "(":
                    s.push(str[i]);                  // 若是是左括號直接壓入堆棧
                    break;
                case "+":
                case "-":
                    if(s.size() != 0){          // 碰到'+' '-',將棧中的全部運算符所有彈出去,直至碰到左括號爲止,輸出到隊列中去
                        temp = s.pop();
                        if (temp.equals("(")) {     // 將左括號放回堆棧,終止循環
                            s.push(temp);
                            s.push(str[i]);
                            break;
                        }
                        else{
                            s.push(str[i]);
                            suffix2 = suffix2 + temp + " ";
                            break;
                        }
                    }
                    else{
                        s.push(str[i]);      // 說明是當前爲第一次進入或者其餘前面運算都有括號等狀況致使棧已經爲空,此時須要將符號進棧
                        break;
                    }
                    // 若是是乘號或者除號,則彈出全部序列,直到碰到加好、減號、左括號爲止,最後將該操做符壓入堆棧
                case "*":
                case "÷":
                    if(s.size()!=0){
                        temp = s.pop();
                        if(temp.equals("+")||temp.equals("-")||temp.equals("(")){
                            s.push(temp);
                            s.push(str[i]);
                            break;
                        }
                        else{
                            s.push(str[i]);
                            suffix2 = suffix2+temp+" ";
                            break;
                        }
                    }
                    else {
                        s.push(str[i]);     //當前爲第一次進入或者其餘前面運算都有括號等狀況致使棧已經爲空,此時須要將符號進棧
                        break;
                    }
                    // 若是碰到的是右括號,則距離棧頂的第一個左括號上面的全部運算符彈出棧並拋棄左括號
                case ")":
                    while (!s.isEmpty()) {
                        temp = s.pop();
                        if (temp.equals("(")) {
                            break;
                        } else {
                            suffix2 = suffix2+temp+" ";
                        }
                    }
                    break;
                // 默認狀況,若是讀取到的是數字,則直接送至輸出序列
                default:
                    suffix2 = suffix2+str[i]+" ";
                    break;
            }

        }
        // 若是堆棧不爲空,則把剩餘運算符一次彈出,送至輸出序列
        while (s.size() != 0) {
            suffix2 = suffix2+s.pop()+" ";
        }
        if(suffix1.equals("")){          //第一個題目
            suffix1 = suffix2;
            suffix = suffix2;
        }
        else{
            if(suffix2.equals(suffix1))
                suffix = "";
            else
                suffix = suffix2;
        }
        suffix1 = suffix2;
        return suffix;
    }
}

運行截圖:

(二)網絡編程與安全-2

實驗要求:

結對編程:1人負責客戶端,一人負責服務器

  1. 注意責任歸宿,要會經過測試證實本身沒有問題
  2. 基於Java Socket實現客戶端/服務器功能,傳輸方式用TCP
  3. 客戶端讓用戶輸入中綴表達式,而後把中綴表達式調用MyBC.java的功能轉化爲後綴表達式,把後綴表達式經過網絡發送給服務器
  4. 服務器接收到後綴表達式,調用MyDC.java的功能計算後綴表達式的值,把結果發送給客戶端
  5. 客戶端顯示服務器發送過來的結果
  6. 上傳測試結果截圖和碼雲連接

知識點:

  • 網絡套接字是基於TCP協議的有鏈接通訊,套接字鏈接就是客戶端的套接字對象和服務器端的套接字對象經過輸入流、輸出流鏈接在一塊兒。服務器創建ServerSocket對象,ServerSocket對象負責等待客戶端請求創建套接字鏈接,而客戶端創建Socket對象服務器發出套接字鏈接請求。
  • 基於UDP的通訊和基於TCP的通訊不一樣,基於UDP的信息傳遞更快,但不提供可靠性保證。

    實驗代碼:

  • 服務器端:
package 實驗五.Assign2;

import 實驗五.Assign1.MyDC;

import java.io.*;
import java.net.*;
public class Sever {
    public static void main(String[] args) {
        ServerSocket serverForClient = null;
        Socket socketOnServer = null;
        DataOutputStream out = null;
        DataInputStream in = null;
        MyDC myDC = new MyDC();
        try{
            serverForClient = new ServerSocket(2010);
        }catch (IOException e1){
            System.out.println("我是豬,我又餓了"+e1);
        }
        try{
            System.out.println("等待小猴投喂");
            socketOnServer = serverForClient.accept();
            out = new DataOutputStream(socketOnServer.getOutputStream());
            in = new DataInputStream(socketOnServer.getInputStream());
            String suffix = in.readUTF();
            System.out.println("胖礫收到小猴的提問"+suffix);
            out.writeUTF(myDC.evaluate(suffix)+"");
            Thread.sleep(500);
        }catch (Exception e){
            System.out.println("小猴已斷開鏈接");
        }
    }
}

運行結果:我負責服務器端

(三)網絡編程與安全-3

實驗要求:

加密結對編程:1人負責客戶端,一人負責服務器

  1. 注意責任歸宿,要會經過測試證實本身沒有問題
  2. 基於Java Socket實現客戶端/服務器功能,傳輸方式用TCP
  3. 客戶端讓用戶輸入中綴表達式,而後把中綴表達式調用MyBC.java的功能轉化爲後綴表達式,把後綴表達式用3DES或AES算法加密後經過網絡把密文發送給服務器
  4. 服務器接收到後綴表達式表達式後,進行解密(和客戶端協商密鑰,能夠用數組保存),而後調用MyDC.java的功能計算後綴表達式的值,把結果發送給客戶端
  5. 客戶端顯示服務器發送過來的結果
  6. 上傳測試結果截圖和碼雲連接

    知識點:

  • DES密鑰
    獲取密鑰生成器,KeyGenerator kg=KeyGenerator.getInstance("DESede")
    初始化密鑰生成器,kg.init(168)
    生成密鑰,SecretKey k=kg.generateKey( )
  • 服務端DES解密
    獲取密鑰長度,String keylength = in.readUTF()
    獲取密鑰
    byte []kb = new byte[Integer.parseInt(keylength)]; for(int i=0; i<Integer.parseInt(keylength); i++){ String t = in.readUTF(); kb[i] = Byte.parseByte(t); }
    獲取密文長度,String clength = in.readUTF()
    獲取密文
    byte []ctext = new byte[Integer.parseInt(clength)]; for(int i=0; i<Integer.parseInt(clength); i++){ String temp = in.readUTF(); ctext[i] = Byte.parseByte(temp); }
    建立密碼器(Cipher對象)Cipher cp=Cipher.getInstance("DESede")
    初始化密碼器cp.init(Cipher.DECRYPT_MODE, k)
    執行解密byte []ptext=cp.doFinal(ctext)

    實驗代碼:服務器端

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.*;
public class Sever1 {
    public static void main(String[] args) {
        ServerSocket serverForClient = null;
        Socket socketOnServer = null;
        DataOutputStream out = null;
        DataInputStream in = null;
        MyDC myDC = new MyDC();
        try{
            serverForClient = new ServerSocket(2010);
        }catch (IOException e1){
            System.out.println("我是豬,我又餓了"+e1);
        }
        try{
            System.out.println("等待小猴投喂");
            socketOnServer = serverForClient.accept();
            out = new DataOutputStream(socketOnServer.getOutputStream());
            in = new DataInputStream(socketOnServer.getInputStream());
            String keylength = in.readUTF();
            byte []kb = new byte[Integer.parseInt(keylength)];
            for(int i=0; i<Integer.parseInt(keylength); i++){
                String t = in.readUTF();
                kb[i] = Byte.parseByte(t);
            }
            String clength = in.readUTF();
            byte []ctext = new byte[Integer.parseInt(clength)];
            for(int i=0; i<Integer.parseInt(clength); i++){
                String temp = in.readUTF();
                ctext[i] = Byte.parseByte(temp);
            }
            SecretKeySpec k=new  SecretKeySpec(kb,"DESede");
            Cipher cp=Cipher.getInstance("DESede");
            cp.init(Cipher.DECRYPT_MODE, k);
            byte []ptext=cp.doFinal(ctext);
            String suffix = new String(ptext,"UTF8");
            System.out.println("胖礫收到小猴的提問"+suffix);
            out.writeUTF(myDC.evaluate(suffix)+"");
            Thread.sleep(500);
        }catch (Exception e){
            System.out.println("小猴已斷開鏈接");
        }
    }
}

實驗結果:

參考:Java密碼學算法

(四)網絡編程與安全-4

實驗要求:

密鑰分發結對編程:1人負責客戶端,一人負責服務器

  1. 注意責任歸宿,要會經過測試證實本身沒有問題
  2. 基於Java Socket實現客戶端/服務器功能,傳輸方式用TCP
  3. 客戶端讓用戶輸入中綴表達式,而後把中綴表達式調用MyBC.java的功能轉化爲後綴表達式,把後綴表達式用3DES或AES算法加密經過網絡把密文發送給服務器
  4. 客戶端和服務器用DH算法進行3DES或AES算法的密鑰交換
  5. 服務器接收到後綴表達式表達式後,進行解密,而後調用MyDC.java的功能計算後綴表達式的值,把結果發送給客戶端
  6. 客戶端顯示服務器發送過來的結果
  7. 上傳測試結果截圖和碼雲連接

知識點:

執行密鑰協定的標準算法是DH算法(Diffie-Hellman算法)。

  • 建立DH公鑰和私鑰
    DH算法是創建在DH公鑰和私鑰的基礎上的, A須要和B共享密鑰時,A和B各自生成DH公鑰和私鑰,公鑰對外公佈而私鑰各自祕密保存,在初始化時須要爲DH指定特定的參數。

程序最後將公鑰和私鑰以對象流的形式保存在文件中,文件名經過命令行參數指定,第一個命令行參數對應的文件保存公鑰,第二個命令行參數對應的文件保存私鑰。

運行程序:

創建兩個目錄A和B,模擬須要祕密通訊的A、B雙方,因爲DH算法須要A和B各自生成DH公鑰和私鑰,所以在這兩個目錄下都拷貝編譯後文件Key_DH。

首先由A建立本身的公鑰和私鑰,即在A目錄下輸入「java Key_DH Apub.dat Apri.dat」運行程序,這時在目錄A下將產生文件Apub.dat和Apri.dat,前者保存着A的公鑰,後者保存着A的私鑰。
而後由B建立本身的公鑰和私鑰,即在B目錄下輸入「java Key_DH Bpub.dat Bpri.dat」運行程序,這時在目錄B下將產生文件Bpub.dat和Bpri.dat,前者保存着B的公鑰,後者保存着B的私鑰。
最後發佈公鑰,A將Apub.dat拷貝到B目錄,B將Bpub.dat拷貝到A的目錄。
這樣,A、B雙方的DH公鑰和私鑰已經建立並部署完畢。

  • 建立共享密鑰
    DH算法中,A能夠用本身的密鑰和B的公鑰按照必定方法生成一個密鑰,B也能夠用本身的密鑰和A的公鑰按照必定方法生成一個密鑰,因爲一些數學規律,這兩個密鑰徹底相同。這樣,A和B間就有了一個共同的密鑰能夠用於各類加密。

Java中KeyAgreement類實現了密鑰協定,它使用init( )方法傳入本身的私鑰,使用doPhase( )方法傳入對方的公鑰,進而可使用generateSecret( )方法生成共享的信息具體步驟以下:

(1) 讀取本身的DH私鑰和對方的DH公鑰

(2) 建立密鑰協定對象

(3) 初始化密鑰協定對象

(4) 執行密鑰協定

(5) 生成共享信息

程序最後將共享信息打印了出來,以便直觀地對比A和B獲得的信息是否相同。將程序KeyAgree編譯後分別拷貝在A和B兩個目錄,首先在A目錄輸入「java KeyAgree Bpub.dat Apri.dat」運行程序,它使用文件Bpub.dat中對方的公鑰和文件Apri.dat中本身的私鑰建立了一段共享的字節數組。

實驗代碼:服務器端

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.*;
public class Server4 {
    public static void main(String[] args) {
        ServerSocket serverForClient = null;
        Socket socketOnServer = null;
        DataOutputStream out = null;
        DataInputStream in = null;
        MyDC myDC = new MyDC();
        try{
            serverForClient = new ServerSocket(2010);
        }catch (IOException e1){
            System.out.println("我是豬,我又餓了"+e1);
        }
        try{
            System.out.println("等待小猴投喂");
            socketOnServer = serverForClient.accept();
            out = new DataOutputStream(socketOnServer.getOutputStream());
            in = new DataInputStream(socketOnServer.getInputStream());
            //獲取密文
            String clength = in.readUTF();
            byte []ctext = new byte[Integer.parseInt(clength)];
            for(int i=0; i<Integer.parseInt(clength); i++){
                String temp = in.readUTF();
                ctext[i] = Byte.parseByte(temp);
            }
            //獲取密鑰
            String keylength = in.readUTF();
            byte []ckey = new byte[Integer.parseInt(keylength)];
            for(int i=0; i<Integer.parseInt(keylength); i++){
                String temp = in.readUTF();
                ckey[i] = Byte.parseByte(temp);
            }
            //密鑰解密
            SecretKeySpec k1 = KeyAgree.KeyAgree("Clientpub.dat","Serverpri.dat");
            Cipher cp=Cipher.getInstance("DESede");
            cp.init(Cipher.DECRYPT_MODE, k1);
            byte []pkey=cp.doFinal(ckey);
            //密文解密
            SecretKeySpec k=new  SecretKeySpec(pkey,"DESede");
            cp.init(Cipher.DECRYPT_MODE, k);
            byte []ptext=cp.doFinal(ctext);
            String suffix = new String(ptext,"UTF8");
            System.out.println("胖礫收到小猴的提問"+suffix);
            out.writeUTF(myDC.evaluate(suffix)+"");
            Thread.sleep(500);
        }catch (Exception e){
            System.out.println("小猴已斷開鏈接");
        }
    }
}

實驗結果:

(五)網絡編程與安全-5

實驗要求:

完整性校驗結對編程:1人負責客戶端,一人負責服務器

  1. 注意責任歸宿,要會經過測試證實本身沒有問題
  2. 基於Java Socket實現客戶端/服務器功能,傳輸方式用TCP
  3. 客戶端讓用戶輸入中綴表達式,而後把中綴表達式調用MyBC.java的功能轉化爲後綴表達式,把後綴表達式用3DES或AES算法加密經過網絡把密文和明文的MD5値發送給服務器
  4. 客戶端和服務器用DH算法進行3DES或AES算法的密鑰交換
  5. 服務器接收到後綴表達式表達式後,進行解密,解密後計算明文的MD5值,和客戶端傳來的MD5進行比較,一致則調用MyDC.java的功能計算後綴表達式的值,把結果發送給客戶端
  6. 客戶端顯示服務器發送過來的結果
  7. 上傳測試結果截圖和碼雲連接

    知識點:

    Java摘要算法- MD5:使用Java計算指定字符串的消息摘要。java.security包中的MessageDigest類提供了計算消息摘要的方法,首先生成對象,執行其update()方法能夠將原始數據傳遞給該對象,而後執行其digest( )方法便可獲得消息摘要。具體步驟以下:

(1) 生成MessageDigest對象
MessageDigest m=MessageDigest.getInstance("MD5");

(2) 傳入須要計算的字符串
m.update(x.getBytes("UTF8" ));

(3) 計算消息摘要
byte s[ ]=m.digest( );

(4) 處理計算結果
必要的話可使用以下代碼將計算結果s轉換爲字符串。
String result=""; for (int i=0; i<s.length; i++){ result+=Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6); }

實驗代碼:

MessageDigest m = MessageDigest.getInstance("MD5");
            m.update(suffix.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);
            }

實驗結果:

碼雲連接

實驗感想:

本次實驗是對以前的知識點的應用,實驗涉及的內容主要是網絡編程方面的,感受難度比較大,不少東西都不是很理解。經過和結對夥伴的共同研究,不斷查閱資料,才最終完成實驗,收穫仍是很大的。

相關文章
相關標籤/搜索