20175221曾祥杰 实验五《网络编程与安全》

实验五《网络编程与安全》

实验报告封面

课程:Java程序设计 班级:1752班 姓名:曾祥杰 学号:20175221

指导教师:娄嘉鹏 实验日期:2019年5月26日

实验时间:13:10 - 15:25 实验序号:21

实验名称:网络编程与安全

实验步骤

  • 第一部分

  • 要求:

  • 两人一组结对编程:

  • 0. 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA

  • 1. 结对实现中缀表达式转后缀表达式的功能 MyBC.java

  • 2. 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java

  • 3. 上传测试代码运行结果截图和码云链接

  • 相关原理:

  • 栈 (Stack)是一种只允许在表尾插入和删除的线性表,有先进后出(FILO),后进先出(LIFO)的特点。允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)。栈的一个应用是用来对四则运算表达式进行求值。
  • 表达式 Exp = S1 + OP + S2 (S1 ,S2是两个操作数,OP为运算符)有三种标识方法:

  • OP + S1 + S2  为前缀表示法
  • S1 + OP + S2  为中缀表示法
  • S1 + S2 + OP  为后缀表示法
  • Java中有Stack类,以及一些方法:
  • empty()

  • push()

  • pop()
  • 具体操作

  • 我们可以使用栈来实现dc 。对逆波兰式求值时,不需要再考虑运算符的优先级,只需从左到右扫描一遍后缀表达式即可。求值伪代码如下:

  • 1.设置一个操作数栈,开始栈为空;
    2.从左到右扫描后缀表达式,遇操作数,进栈;
    3.若遇运算符,则从栈中退出两个元素,先退出的放到运算符的右边,后退出的放到运算符左边,运算后的结果再进栈,直到后缀表达式扫描完毕。
    4.此时,栈中仅有一个元素,即为运算的结果。
  • MyBC.java

  • import java.util.*;
    /**
     * @author 20175221 Zxj
     */
    public class MyBC {
        private Stack<String> stack;
        private List<String> list;
        private String information;
        private String Information = "";
        public MyBC() {
            stack = new Stack<String>();//设立一个栈,存放运算符
            list = new ArrayList<String>();//创建一个表,存放操作数及运算符
        }
        public void conversion(String exp) {   //中缀转后缀
            String element ;
            StringTokenizer tokenizer = new StringTokenizer(exp);
            while (tokenizer.hasMoreTokens()) {  //当tokenizer有下一个值时,进行循环,并把值赋给element
                element  = tokenizer.nextToken();
                if (element.equals("(")) {  //若是左括号,入栈
                    stack.push(element);
                }
                else if (element.equals("+") || element.equals("-")) {  //若是“+”或“-”,继续判断栈是否为空
                    if (!stack.empty()) {  //若栈非空,判断栈顶元素
                        if (stack.peek().equals("(")) {  //若栈顶为“(”,运算符入栈
                            stack.push(element);
                        }
                        else {  //否则先把栈顶元素移除,加到表中,再将运算符入栈
                            list.add(stack.pop());
                            stack.push(element);
                        }
                    }
                    else {  //若栈为空,运算符入栈
                        stack.push(element);
                    }
                }
                else if (element.equals("*") || element.equals("/")) {  //若是“*”或“/”,继续判断栈是否为空
                    if (!stack.empty()) {  //若栈非空,判断栈顶元素是什么
                        if (stack.peek().equals("*") || stack.peek().equals("/")) {  //若栈顶为“*”或“/”,先把栈顶元素移除,加到表中,再将运算符入栈
                            list.add(stack.pop());
                            stack.push(element);
                        }
                        else {  //若栈顶为其他,运算符直接入栈
                            stack.push(element);
                        }
                    }
                    else {  //若栈为空,运算符直接入栈
                        stack.push(element);
                    }
                }
                else if (element.equals(")")) {  //若遇到“)”,开始循环
                    while (true) {  //先把栈顶元素移除并赋给temp
                        String temp = stack.pop();
                        if (!temp.equals("(")) {  //若temp不为“(”,则加到表
                            list.add(temp);
                        }
                        else {  //若temp为“(”,退出循环
                            break;
                        }
                    }
                }
                else {  //若为操作数,进入列表
                    list.add(element);
                }
            }
            while (!stack.empty()) {  //将栈中元素取出,加到列表中,直到栈为空
                list.add(stack.pop());
            }
            ListIterator<String> List = list.listIterator();  //返回此列表元素的列表迭代器
            while (List.hasNext()) {  //将迭代器中的元素依次取出,并加上空格作为分隔符
                Information += List.next() + " ";
                List.remove();
            }
            information = Information;
        }
    
        public String getInformation() {
            return information;
        }
    }
  • MyDC.java

  • import java.util.*;
    
    public class MyDC {
        private final char ADD = '+';
        private final char SUBTRACT = '-';
        private final char MUTIPLY = '*';
        private final char DIVIDE = '/';
        private Stack<Integer> stack;
        public MyDC(){
            stack = new Stack<Integer>();
        }
        public int evaluate(String exp){
            int op1,op2,result = 0;
            String element;
            StringTokenizer tokenizer = new StringTokenizer(exp);
            while(tokenizer.hasMoreTokens()){
                element = tokenizer.nextToken();
                if(isOperator(element)){
                    op2 = (stack.pop().intValue());
                    op1 = (stack.pop().intValue());
                    result = evalSingleOp(element.charAt(0),op1,op2);
                    stack.push(new Integer(result));
                }
                else {
                    stack.push(new Integer((Integer.parseInt(element))));
                }
            }
            return result;
        }
        private boolean isOperator(String element){
            return (element.equals("+")||element.equals("-")||element.equals("*")||element.equals("/"));
        }
        private int evalSingleOp(char operation,int op1,int op2) {
            int result = 0;
            switch (operation){
                case ADD:
                    result = op1+op2;
                    break;
                case SUBTRACT:
                    result = op1-op2;
                    break;
                case MUTIPLY:
                    result = op1*op2;
                    break;
                case DIVIDE:
                    result = op1/op2;
            }
            return result;
        }
    }
  • MyDCTest.java

  • import java.util.*;
    public class MyDCTester {
        public static void main(String[] args) {
            int result;
            String exp;
            MyBC bc = new MyBC();
            MyDC dc = new MyDC();
            System.out.println("请输入中缀表达式:");
            Scanner in = new Scanner(System.in);
            exp = in.nextLine();
            bc.conversion(exp);
            System.out.println("后缀表达式为:
    "+bc.getInformation());
            result = dc.evaluate(bc.getInformation());
            System.out.println("计算结果为:
    "+result);
        }
    }
  • 运行如下:
  • 第二部分

  • 要求:

  •  结对编程:1人负责客户端,一人负责服务器

  • 0. 注意责任归宿,要会通过测试证明自己没有问题

  • 1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP

  • 2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器

  • 3. 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端

  • 4. 客户端显示服务器发送过来的结果

  • 5. 上传测试结果截图和码云链接

  • 相关原理:

  • 套接字是一个网络连接的端点。在java中,使用java.net.Socket对象来表示一个套接字。
  • 客户端套接字 Socket clientSocket = new Socket("服务器IP地址",端口号) 
  • 服务器端套接字 SeverSocket severForClient = new SeverSocket(端口号) 
  • 使用 close() 关闭套接字链接
  • 具体操作:

  • 我和 20175120 结对编程,我负责客户端
  • 首先查看自己电脑的IP地址:
  • Address.java

  • import java.net.*;
    
    public class Address {
        public static void main(String[] args) throws UnknownHostException {
            InetAddress net = InetAddress.getLocalHost();
            System.out.println(net.toString());
        }
    }


     

  • Client.java

  • import java.io.*;
    import java.net.*;
    
    public class Client {
        public static void main(String args[]) {
            System.out.println("20175221 正在启动...");//向服务器端发送信息
            Socket mysocket;//创建客户端Socket
            DataInputStream in = null;
            DataOutputStream out = null;
            try {
                mysocket = new Socket("169.254.245.151", 2010);//客户端指向服务器地址和端口
                in = new DataInputStream(mysocket.getInputStream());
                out = new DataOutputStream(mysocket.getOutputStream());
                System.out.println("请输入中缀表达式:");//向本机的2010端口发出客户请求
                String string = new BufferedReader(new InputStreamReader(System.in)).readLine();
                MyBC conv = new MyBC();
                conv.conversion(string);
                String string1 = conv.getInformation();
                out.writeUTF(string1);
                String reply = in.readUTF();//in读取信息,堵塞状态
                System.out.println("20175221 收到 20175120 的回答:
    " + reply);// 从Server读入字符串,并打印
                Thread.sleep(2000);
            } catch (Exception e) {
                System.out.println("20175120 已掉线" + e);//输出异常
            }
        }
    }
  • Server.java

  • import java.io.*;
    import java.net.*;
    
    public class Server {
        public static void main(String[] args) throws IOException {
            int reply;
            ServerSocket serverForClient = null;
            Socket socketOnServer = null;
            DataOutputStream out = null;
            DataInputStream in = null;
            try {
                serverForClient = new ServerSocket(2010);
            } catch (IOException e1) {
                System.out.println(e1);
            }
            try {
                System.out.println("等待 20175221 呼叫...");
                socketOnServer = serverForClient.accept(); //堵塞状态,除非20175221呼叫
                in = new DataInputStream(socketOnServer.getInputStream());
                out = new DataOutputStream(socketOnServer.getOutputStream());
                String information = in.readUTF(); // in读取信息,堵塞状态
                System.out.println("20175120 收到 20175221 的提问:" + information);
                MyDC mydc = new MyDC();
                reply = mydc.evaluate(information);
                out.writeUTF(reply + "");
                Thread.sleep(2000);
            } catch (Exception e) {
                System.out.println("20175221 已掉线" + e);
            }
        }
    }
  • 运行如下:
  • 第三部分

  • 要求:

  • 加密结对编程:1人负责客户端,一人负责服务器
  • 0. 注意责任归宿,要会通过测试证明自己没有问题
  • 1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
  • 2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
  • 3. 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  • 4. 客户端显示服务器发送过来的结果
  • 5. 上传测试结果截图和码云链接
  • 相关原理:

  • 由于老师曾经在课上就让我们练习过用DES加解密自己的学号姓名,所以相较AES密码算法,DES比较熟悉,因此我们选择DES。
  • DES算法参考娄老师的博客“Java 密码学算法
  • 实现DES加密主要有以下几个步骤:
  • 对称密钥的生成和保存;
  • 使用对称密钥进行加密和解密;
  • 从文件中获取加密时使用的密钥,使用密钥进行解密。
  • 具体操作:

  • 编程思路:
  • 1.获取密钥生成器

  •  KeyGenerator kg=KeyGenerator.getInstance("DESede"); 
  • 2.初始化密钥生成器
  •  kg.init(168); 
  • 3. 生成密钥
  •  SecretKey k=kg.generateKey( ); 
  • 4.通过对象序列化方式将密钥保存在文件中
  • FileOutputStream f=new FileOutputStream("key1.dat");
     ObjectOutputStream b=new ObjectOutputStream(f);
     b.writeObject(k);
  • 我依旧负责的是客户端, Client.java 在刚才的基础上增加了使用DES算法加密的部分,实现DES的加密
  • Client.java

  • import javax.crypto.Cipher;
    import java.io.*;
    import java.net.Socket;
    import java.security.Key;
    import java.util.Scanner;
    
    public class Client {
        public static void main(String args[]) {
            Socket mysocket;
            MyBC mybc = new MyBC();
            DataInputStream in = null;
            DataOutputStream out = null;
            Scanner scanner = new Scanner(System.in);
            String string;
            try {
                mysocket = new Socket("169.254.245.151", 5221);
                in = new DataInputStream(mysocket.getInputStream());
                out = new DataOutputStream(mysocket.getOutputStream());
                System.out.println("20175221 正在启动...");
                FileInputStream f = new FileInputStream("key1.dat");
                ObjectInputStream b = new ObjectInputStream(f);
                Key key = (Key) b.readObject();
                Cipher cp = Cipher.getInstance("DESede");
                cp.init(Cipher.ENCRYPT_MODE, key);
                System.out.println("请输入中缀表达式:");
                string = scanner.nextLine();
                mybc.conversion(string);
                String string1 = mybc.getInformation();
                byte ptext[] = string1.getBytes("UTF-8");
                byte ctext[] = cp.doFinal(ptext);
                System.out.println("加密后的后缀表达式:");
                for (int i = 0; i < ctext.length; i++) {
                    System.out.print(ctext[i] + ",");
                }
                System.out.println("");
                out.writeUTF(ctext.length + "");
                for (int i = 0; i < ctext.length; i++) {
                    out.writeUTF(ctext[i] + "");
                }
                String s = in.readUTF();
                System.out.println("20175221 收到 20175120 的回应:
    " + s);
            } catch (Exception e) {
                System.out.println("20175120 已掉线..." + e);
            }
        }
    }
  • Server.java

  • import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class Server {
        public static void main(String args[]) {
            MyDC mydc = new MyDC();
            ServerSocket serverForClient = null;
            Socket socketOnServer = null;
            DataOutputStream out = null;
            DataInputStream in = null;
            try {
                serverForClient = new ServerSocket(5221);
            } catch (IOException e1) {
                System.out.println(e1);
            }
            try {
                System.out.println("等待 20175221 下达命令...");
                socketOnServer = serverForClient.accept(); //堵塞状态,除非 20175221 呼叫
                System.out.println("20175221 终于连接上了");
                out = new DataOutputStream(socketOnServer.getOutputStream());
                in = new DataInputStream(socketOnServer.getInputStream());
                String str = in.readUTF(); // in读取信息,堵塞状态
                byte ctext[] = new byte[Integer.parseInt(str)];
                for (int i = 0;i<Integer.parseInt(str);i++) {
                    String temp = in.readUTF();
                    ctext[i] = Byte.parseByte(temp);
                }
                // 获取密钥
                FileInputStream f = new FileInputStream("keykb1.dat");
                int num = f.available();
                byte[] keykb = new byte[num];
                f.read(keykb);
                SecretKeySpec k = new SecretKeySpec(keykb, "DESede");
                // 解密
                Cipher cp = Cipher.getInstance("DESede");
                cp.init(Cipher.DECRYPT_MODE, k);
                byte[] ptext = cp.doFinal(ctext);
                System.out.println("");
                // 显示明文
                String p = new String(ptext,"UTF8");
                String s = mydc.evaluate(p)+"";
                System.out.println("解密后的后缀表达式:
    " + p);
                System.out.println("开始计算后缀表达式:
    " + p);
                System.out.println("后缀表达式的值为:
    " + s);
                out.writeUTF(mydc.evaluate(p)+"");
                System.out.println("20175120 正在将值传给 20175221...");
            } catch (Exception e) {
                System.out.println("20175221 已掉线..." + e);
            }
        }
    }


     

  • 运行如下:

  • 第四部分
  • 要求:

  • 密钥分发结对编程:1人负责客户端,一人负责服务器
  • 0. 注意责任归宿,要会通过测试证明自己没有问题
  • 1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
  • 2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
  • 3. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
  • 4. 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  • 5. 客户端显示服务器发送过来的结果
  • 6. 上传测试结果截图和码云链接
  • 相关原理:

  • DH算法大致分为以下两步:
  • 1.创建DH公钥和私钥;
  • 2.创建共享密钥。
  • 具体操作:

  • Client.java

  • import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.*;
    import java.net.Socket;
    import java.security.Key;
    import java.util.Scanner;
    
    public class Client {
        public static void main(String args[]) {
            MyBC mybc = new MyBC();
            Socket mysocket;
            DataInputStream in = null;
            DataOutputStream out = null;
            Scanner scanner = new Scanner(System.in);
            String str;
            try {
                mysocket = new Socket("169.254.245.151", 5221);
                System.out.println("20175221 正在启动...");
                in = new DataInputStream(mysocket.getInputStream());
                out = new DataOutputStream(mysocket.getOutputStream());
                System.out.println("请输入中缀表达式:");
                str = scanner.nextLine();
                Key_DH.DH("Cpub.dat","Cpri.dat");
                FileInputStream fp = new FileInputStream("Cpub.dat");
                ObjectInputStream bp = new ObjectInputStream(fp);
                Key kp = (Key) bp.readObject();
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(kp);
                byte[] kb = baos.toByteArray();
                out.writeUTF(kb.length + "");
                for (int i = 0; i < kb.length; i++) {
                    out.writeUTF(kb[i] + "");
                }
                Thread.sleep(1000);
                int len = Integer.parseInt(in.readUTF());
                byte np[] = new byte[len];
                for (int i = 0;i<len;i++) {
                    String temp = in.readUTF();
                    np[i] = Byte.parseByte(temp);
                }
                ObjectInputStream ois = new ObjectInputStream (new ByteArrayInputStream (np));
                Key k2 = (Key)ois.readObject();;
                FileOutputStream f2 = new FileOutputStream("Spub.dat");
                ObjectOutputStream b2 = new ObjectOutputStream(f2);
                b2.writeObject(k2);
                KeyAgree.DH("Spub.dat","Cpri.dat");
                FileInputStream f = new FileInputStream("sb.dat");
                byte[] keysb = new byte[24];
                f.read(keysb);
                System.out.println("公共密钥:");
                for (int i = 0;i<24;i++) {
                    System.out.print(keysb[i]+",");
                }
                System.out.println("");
                SecretKeySpec k = new SecretKeySpec(keysb, "DESede");
                Cipher cp = Cipher.getInstance("DESede");
                cp.init(Cipher.ENCRYPT_MODE, k);
                mybc.conversion(str);
                String str1 = mybc.getInformation();
                byte ptext[] = str1.getBytes("UTF-8");
                byte ctext[] = cp.doFinal(ptext);
                System.out.println("已加密后缀表达式:");
                for (int i = 0; i < ctext.length; i++) {
                    System.out.print(ctext[i] + ",");
                }
                System.out.println("");
                out.writeUTF(ctext.length + "");
                for (int i = 0; i < ctext.length; i++) {
                    out.writeUTF(ctext[i] + "");
                }
                String s = in.readUTF();   //in读取信息,堵塞状态
                System.out.println("20175221 收到 20175120 的回应:" + s);
            } catch (Exception e) {
                System.out.println("20175120 已掉线..." + e);
            }
        }
    }
    • Server.java

    • import javax.crypto.Cipher;
      import javax.crypto.spec.SecretKeySpec;
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      import java.security.Key;
      
      public class Server {
          public static void main(String args[]) {
              MyDC mydc = new MyDC();
              ServerSocket serverForClient = null;
              Socket socketOnServer = null;
              DataOutputStream out = null;
              DataInputStream in = null;
              try {
                  serverForClient = new ServerSocket(5221);
              } catch (IOException e1) {
                  System.out.println(e1);
              }
              try {
                  System.out.println("等待 20175221 下达命令...");
                  socketOnServer = serverForClient.accept(); //堵塞状态,除非 20175221 呼叫
                  System.out.println("20175221 终于连接上了!");
                  out = new DataOutputStream(socketOnServer.getOutputStream());
                  in = new DataInputStream(socketOnServer.getInputStream());
                  Key_DH.DH("Spub.dat","Spri.dat");
                  int len = Integer.parseInt(in.readUTF());
                  byte np[] = new byte[len];
                  for (int i = 0;i<len;i++) {
                      String temp = in.readUTF();
                      np[i] = Byte.parseByte(temp);
                  }
                  ObjectInputStream ois = new ObjectInputStream (new ByteArrayInputStream (np));
                  Key k2 = (Key)ois.readObject();
                  FileOutputStream f2 = new FileOutputStream("Cpub.dat");
                  ObjectOutputStream b2 = new ObjectOutputStream(f2);
                  b2.writeObject(k2);
                  FileInputStream fp = new FileInputStream("Spub.dat");
                  ObjectInputStream bp = new ObjectInputStream(fp);
                  Key kp = (Key) bp.readObject();
                  ByteArrayOutputStream baos = new ByteArrayOutputStream();
                  ObjectOutputStream oos = new ObjectOutputStream(baos);
                  oos.writeObject(kp);
                  byte[] kb = baos.toByteArray();
                  out.writeUTF(kb.length + "");
                  for (int i = 0; i < kb.length; i++) {
                      out.writeUTF(kb[i] + "");
                  }
                  KeyAgree.DH("Cpub.dat","Spri.dat");
                  String leng = in.readUTF(); // in读取信息,堵塞状态
                  byte ctext[] = new byte[Integer.parseInt(leng)];
                  for (int i = 0;i<Integer.parseInt(leng);i++) {
                      String temp = in.readUTF();
                      ctext[i] = Byte.parseByte(temp);
                  }
                  // 获取密钥
                  FileInputStream f = new FileInputStream("sb.dat");
                  byte[] keysb = new byte[24];
                  f.read(keysb);
                  System.out.println("公共密钥:");
                  for (int i = 0;i<24;i++) {
                      System.out.print(keysb[i]+",");
                  }
                  System.out.println("");
                  SecretKeySpec k = new SecretKeySpec(keysb, "DESede");
                  // 解密
                  Cipher cp = Cipher.getInstance("DESede");
                  cp.init(Cipher.DECRYPT_MODE, k);
                  byte[] ptext = cp.doFinal(ctext);
                  System.out.println("");
                  // 显示明文
                  String p = new String(ptext,"UTF8");
                  String s = mydc.evaluate(p)+"";
                  System.out.println("解密后的后缀表达式:" + p);
                  System.out.println("开始计算后缀表达式: " + p);
                  System.out.println("后缀表达式的值为:"+ s);
                  out.writeUTF(mydc.evaluate(p)+"");
                  System.out.println("20175120 正在将值传给 20175221...");
              } catch (Exception e) {
                  System.out.println("20175221 已掉线..." + e);
              }
          }
      }


       

      • 运行如下

      • 第五部分

      • 要求:

      • 完整性校验结对编程:1人负责客户端,一人负责服务器
      • 0. 注意责任归宿,要会通过测试证明自己没有问题
      • 1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
      • 2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
      • 3. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
      • 4. 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
      • 5. 客户端显示服务器发送过来的结果
      • 6. 上传测试结果截图和码云链接
      • 相关原理:                        

      • 可以使用Java计算指定字符串的消息摘要

      •  update() 可以将原始数据传递给该对象

      •  digest() 可以得到消息摘要    

      • 具体操作:

      • Client.java

      • import javax.crypto.Cipher;
        import javax.crypto.spec.SecretKeySpec;
        import java.io.*;
        import java.net.Socket;
        import java.security.Key;
        import java.util.Scanner;
        
        public class Client {
            public static void main(String args[]) {
                MyBC mybc = new MyBC();
                Socket mysocket;
                DataInputStream in = null;
                DataOutputStream out = null;
                Scanner scanner = new Scanner(System.in);
                String str;
                try {
                    mysocket = new Socket("169.254.245.151", 5221);
                    System.out.println("20175221 正在启动...");
                    in = new DataInputStream(mysocket.getInputStream());
                    out = new DataOutputStream(mysocket.getOutputStream());
                    System.out.println("请输入中缀表达式:");
                    str = scanner.nextLine();
                    Key_DH.DH("Cpub.dat","Cpri.dat");
                    FileInputStream fp = new FileInputStream("Cpub.dat");
                    ObjectInputStream bp = new ObjectInputStream(fp);
                    Key kp = (Key) bp.readObject();
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ObjectOutputStream oos = new ObjectOutputStream(baos);
                    oos.writeObject(kp);
                    byte[] kb = baos.toByteArray();
                    out.writeUTF(kb.length + "");
                    for (int i = 0; i < kb.length; i++) {
                        out.writeUTF(kb[i] + "");
                    }
                    Thread.sleep(1000);
                    int len = Integer.parseInt(in.readUTF());
                    byte np[] = new byte[len];
                    for (int i = 0;i<len;i++) {
                        String temp = in.readUTF();
                        np[i] = Byte.parseByte(temp);
                    }
                    ObjectInputStream ois = new ObjectInputStream (new ByteArrayInputStream (np));
                    Key k2 = (Key)ois.readObject();;
                    FileOutputStream f2 = new FileOutputStream("Spub.dat");
                    ObjectOutputStream b2 = new ObjectOutputStream(f2);
                    b2.writeObject(k2);
        
                    KeyAgree.DH("Spub.dat","Cpri.dat");
                    FileInputStream f = new FileInputStream("sb.dat");
                    byte[] keysb = new byte[24];
                    f.read(keysb);
                    System.out.println("公共密钥:");
                    for (int i = 0;i<24;i++) {
                        System.out.print(keysb[i]+",");
                    }
                    System.out.println("");
                    SecretKeySpec k = new SecretKeySpec(keysb, "DESede");
                    Cipher cp = Cipher.getInstance("DESede");
                    cp.init(Cipher.ENCRYPT_MODE, k);
        
                    mybc.conversion(str);
                    String str1 = mybc.getInformation();
                    byte ptext[] = str1.getBytes("UTF-8");
                    String ptextMd5 = DigestPass.DP(str1);
                    System.out.println("明文的MD5值为:
        "+ptextMd5);
                    byte ctext[] = cp.doFinal(ptext);
                    System.out.println("加密后的后缀表达式:");
                    for (int i = 0; i < ctext.length; i++) {
                        System.out.print(ctext[i] + ",");
                    }
                    System.out.println("");
                    out.writeUTF(ctext.length + "");
                    for (int i = 0; i < ctext.length; i++) {
                        out.writeUTF(ctext[i] + "");
                    }
                    out.writeUTF(ptextMd5);
                    String s = in.readUTF();   //in读取信息,堵塞状态
                    System.out.println("20175221 收到 20175120 的回应:
        " + s);
                } catch (Exception e) {
                    System.out.println("20175120 已掉线..." + e);
                }
            }
        }
      • Server.java

      • import javax.crypto.Cipher;
        import javax.crypto.spec.SecretKeySpec;
        import java.io.*;
        import java.net.ServerSocket;
        import java.net.Socket;
        import java.security.Key;
        
        public class Server {
            public static void main(String args[]) {
                MyDC mydc = new MyDC();
                ServerSocket serverForClient = null;
                Socket socketOnServer = null;
                DataOutputStream out = null;
                DataInputStream in = null;
                try {
                    serverForClient = new ServerSocket(5221);
                } catch (IOException e1) {
                    System.out.println(e1);
                }
                try {
                    System.out.println("等待 20175221 呼叫...");
                    socketOnServer = serverForClient.accept(); //堵塞状态,除非有客户呼叫
                    System.out.println("20175221 终于连接上了!");
                    out = new DataOutputStream(socketOnServer.getOutputStream());
                    in = new DataInputStream(socketOnServer.getInputStream());
        
                    Key_DH.DH("Spub.dat","Spri.dat");
                    int len = Integer.parseInt(in.readUTF());
                    byte np[] = new byte[len];
                    for (int i = 0;i<len;i++) {
                        String temp = in.readUTF();
                        np[i] = Byte.parseByte(temp);
                    }
                    ObjectInputStream ois = new ObjectInputStream (new ByteArrayInputStream(np));
                    Key k2 = (Key)ois.readObject();;
                    FileOutputStream f2 = new FileOutputStream("Cpub.dat");
                    ObjectOutputStream b2 = new ObjectOutputStream(f2);
                    b2.writeObject(k2);
        
                    FileInputStream fp = new FileInputStream("Spub.dat");
                    ObjectInputStream bp = new ObjectInputStream(fp);
                    Key kp = (Key) bp.readObject();
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ObjectOutputStream oos = new ObjectOutputStream(baos);
                    oos.writeObject(kp);
                    byte[] kb = baos.toByteArray();
                    out.writeUTF(kb.length + "");
                    for (int i = 0; i < kb.length; i++) {
                        out.writeUTF(kb[i] + "");
                    }
        
                    KeyAgree.DH("Cpub.dat","Spri.dat");
        
                    String leng = in.readUTF(); // in读取信息,堵塞状态
                    byte ctext[] = new byte[Integer.parseInt(leng)];
                    for (int i = 0;i<Integer.parseInt(leng);i++) {
                        String temp = in.readUTF();
                        ctext[i] = Byte.parseByte(temp);
                    }
                    String check = in.readUTF();
                    // 获取密钥
                    FileInputStream f = new FileInputStream("sb.dat");
                    byte[] keysb = new byte[24];
                    f.read(keysb);
                    System.out.println("公共密钥:");
                    for (int i = 0;i<24;i++) {
                        System.out.print(keysb[i]+",");
                    }
                    System.out.println("");
                    SecretKeySpec k = new SecretKeySpec(keysb, "DESede");
                    // 解密
                    Cipher cp = Cipher.getInstance("DESede");
                    cp.init(Cipher.DECRYPT_MODE, k);
                    byte[] ptext = cp.doFinal(ctext);
                    System.out.println("");
                    // 显示明文
                    String p = new String(ptext, "UTF8");
                    String s = mydc.evaluate(p)+"";
                    String pMd5 = DigestPass.DP(p);
                    System.out.println("解密得到明文的MD5值:
        "+pMd5);
                    if (pMd5.equals(check)){
                        System.out.println("已验证与 20175221 的MD5值一致");
                        System.out.println("计算后缀表达式: 
        " + p);
                        out.writeUTF(mydc.evaluate(p)+"");
                        System.out.println("后缀表达式的值为:
        "+ s);
                    }
                    else {
                        System.out.println("警告!!! MD5值不一致!你是不是背叛了 20175221!");
                    }
                } catch (Exception e) {
                    System.out.println("20175221 已掉线..." + e);
                }
            }
        }
      • 运行如下:
      •  
  • 码云链接

实验体会:

  • 此次实验的内容是网络编程,同时要求两人结对完成。基本上所有的实验点都与客户端和服务器有关,我主要负责客户端,我的搭档负责服务器。这就要求我们俩要配合默契,不能有一个人没有完成指定的任务,否则就达不到实验点的要求。
  • 在这次实验之前,虽然有看过一些Java网络编程理论的内容。但等我真正自己动手操作时,发现其实还是有许多与预想不一样的地方,这就是慢慢修改,考验耐心的时刻,但很庆幸我挺了过来。
  • 在和搭档一起做实验的过程中,我们也复习了网络编程和密码学算法的相关知识,参考了书上代码和以及娄老师的博客---Java 密码学算法,我们着实在这个过程中学到了很多新的内容。
  • 这次结对编程,相当于减轻了一半的工作量。为了给这次实验增添一些乐趣,我们也是尽可能把一些语言丰富。
  • 这是Java的最后一次实验,我收获颇丰,感激这一段学习Java的时间。
步骤耗时百分比
需求分析 10min 5%
设计 40min 20%
代码实现 100min 50%
测试 30min 15%
分析总结 20min 10%

 参考资料

原文地址:https://www.cnblogs.com/zxja/p/10925619.html