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("后缀表达式为:\n"+bc.getInformation()); result = dc.evaluate(bc.getInformation()); System.out.println("计算结果为:\n"+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 的回答:\n" + 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 的回应:\n" + 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("解密后的后缀表达式:\n" + p); System.out.println("开始计算后缀表达式:\n" + p); System.out.println("后缀表达式的值为:\n" + 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值为:\n"+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 的回应:\n" + 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值:\n"+pMd5); if (pMd5.equals(check)){ System.out.println("已验证与 20175221 的MD5值一致"); System.out.println("计算后缀表达式: \n" + p); out.writeUTF(mydc.evaluate(p)+""); System.out.println("后缀表达式的值为:\n"+ 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% |