实验内容及步骤
任务一:两人一组结对编程:
- 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA
- 结对实现中缀表达式转后缀表达式的功能 MyBC.java
- 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
- 上传测试代码运行结果截图和码云链接
实现中缀表达式转后缀表达式
import java.util.Stack; import java.util.StringTokenizer; public class MyBC { /** constant for addition symbol */ private final char ADD = '+'; /** constant for subtraction symbol */ private final char SUBTRACT = '-'; /** constant for multiplication symbol */ private final char MULTIPLY = '*'; /** constant for division symbol */ private final char DIVIDE = '/'; /** the stack */ Stackstack=new Stack ();; String expression; public void setExpression(String str) { expression=str; } public String changedWay() { String changedExpression = ""; Stack signStack = new Stack();// 操作符栈 for (int i = 0; i < expression.length(); i++) { char c = expression.charAt(i); if (c >= '0' && c <= '9') { changedExpression=changedExpression+c; } else if (c == '+' || c == '-' || c == '*' || c == '/') { changedExpression=changedExpression+" ";//分隔数字 if (signStack.empty()) { signStack.push(c); } else if (judgeValue(c) >= judgeValue((Character) signStack.peek())) {//优先级高于或等于,运算符号均进栈 signStack.push(c); } else { changedExpression=changedExpression+(char)signStack.pop(); signStack.push(c); } } else if (c=='(') { signStack.push(c); } else if (c==')') { while((char)signStack.peek()!='(') { changedExpression=changedExpression+" "+signStack.pop(); } signStack.pop(); } } while(!signStack.empty()){ changedExpression=changedExpression+" "+String.valueOf(signStack.pop()); } return changedExpression; } private static int judgeValue(char c) { int value = 0; switch (c) { case '(': value = 1; break; case '+': case '-': value = 2; break; case '*': case '/': value = 3; break; case ')': value = 4; default: value = 0; } return value; } public int evaluate (String expr) {//后缀表达式的运算方法 int op1, op2, result = 0; String token; StringTokenizer tokenizer = new StringTokenizer (expr);//使用StringTokenizer类分解String对象的字符序列,默认为空格符... //此时tokenizer为一个分析器 while (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken(); if (isOperator(token)) { op2 = (stack.pop()).intValue();//出栈 op1 = (stack.pop()).intValue();//出栈 result = evalSingleOp (token.charAt(0), op1, op2);//String对象第一个字符转换为char类型的方法为:str.charAt(0) stack.push (new Integer(result));//进栈 } else { stack.push(new Integer(Integer.parseInt(token)));//进栈 } } return result; } private boolean isOperator (String token) { return ( token.equals("+") || token.equals("-") || token.equals("*") || token.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 MULTIPLY: result = op1 * op2; break; case DIVIDE: result = op1 / op2; } return result; } }
- 实现截图为:
任务二:两人一组结对编程:
- 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA
- 结对实现中缀表达式转后缀表达式的功能 MyBC.java
- 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
- 上传测试代码运行结果截图和码云链接
- 获取本机IP地址:DESKTOP-HN1B8O2/172.30.2.248
netAddress address_3=InetAddress.getLocalHost();
System.out.println(address_3.toString());
- 结对伙伴的IP为:172.30.4.50 所选端口为:1111
- 负责客服端的构建,实现代码为:
- 选用AES加密算法
import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.Socket; import java.util.*; public class Client_2Socket { public static void main(String[] args) { Socket Client_2Socket; DataInputStream Client_2in=null; DataOutputStream Client_2out=null; String expr,str; Scanner scanner=new Scanner(System.in); System.out.println("请输入表达式:"); str=scanner.nextLine(); MyBC mybc=new MyBC(); mybc.setExpression(str); expr=mybc.changedWay(); try { Client_2Socket=new Socket("172.30.2.248",5353); Client_2in=new DataInputStream(Client_2Socket.getInputStream()); Client_2out=new DataOutputStream(Client_2Socket.getOutputStream()); Client_2out.writeUTF(expr); String s=Client_2in.readUTF(); System.out.println("服务器回复:\n"+s); } catch (Exception e) { System.out.println("服务器已断开"+e); } } }
- 结对伙伴负责的服务器端代码为:
import java.io.*; import java.net.*; import java.util.*; public class Server_2 { public static void main(String[] args) { ServerSocket Server_2forClient_2=null; Socket SocketOnServer_2=null; DataOutputStream Server_2out=null; DataInputStream Server_2in=null; try { Server_2forClient_2=new ServerSocket(5353); } catch (IOException e1) { System.out.println(e1); //e1.printStackTrace(); } try { System.out.println("等待客户端呼叫……"); SocketOnServer_2=Server_2forClient_2.accept(); Server_2out=new DataOutputStream(SocketOnServer_2.getOutputStream()); Server_2in=new DataInputStream(SocketOnServer_2.getInputStream()); String expr=Server_2in.readUTF(); System.out.println("服务器接收到表达式:"+expr); int result; MyBC mybc=new MyBC(); result=mybc.evaluate(expr); Server_2out.writeUTF("后缀表达式:"+expr+",运算结果为:"+result); Thread.sleep(500); } catch (Exception e2) { System.out.println("客户端已断开"+e2); } } }
- 实验截图
任务三:加密结对编程:1人负责客户端,一人负责服务器
- 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
- 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
- 思路:密文通过TCP传输,而AES密钥通过机密通道传输。
- 负责客服端的构建,代码为:
import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.Socket; import java.util.*; public class Client_3 { public static void main(String[] args) { Socket Client_2Socket; DataInputStream Client_2in=null; DataOutputStream Client_2out=null; String expr=null; String str=null; String Ciphertext=null; Scanner scanner=new Scanner(System.in); System.out.println("请输入表达式:"); str=scanner.nextLine(); MyBC mybc=new MyBC(); mybc.setExpression(str); expr=mybc.changedWay(); try { AES.produceAESKey();//生成AES密钥 byte[]cc= AES.EncryptionAES(expr);//需要传输的密文,数组形式传输。 Ciphertext = Base64.getEncoder().encodeToString(cc);//将加密后的密文由byte[]转换为String类型 } catch (Exception e) { e.printStackTrace(); } try { Client_2Socket=new Socket("172.30.2.248",5300); Client_2in=new DataInputStream(Client_2Socket.getInputStream()); Client_2out=new DataOutputStream(Client_2Socket.getOutputStream()); Client_2out.writeUTF(Ciphertext); String s=Client_2in.readUTF(); System.out.println("服务器回复:\n"+s); } catch (Exception e) { System.out.println("服务器已断开"+e); } } }
- 结对伙伴负责构建服务器,代码为:
import sun.security.krb5.internal.crypto.Aes128; import java.io.*; import java.net.*; import java.util.*; public class Server_3 { public static void main(String[] args) { ServerSocket Server_2forClient_2=null; Socket SocketOnServer_2=null; DataOutputStream Server_2out=null; DataInputStream Server_2in=null; try { Server_2forClient_2=new ServerSocket(5300); } catch (IOException e1) { System.out.println(e1); } try { System.out.println("等待客户端呼叫……"); SocketOnServer_2=Server_2forClient_2.accept(); Server_2out=new DataOutputStream(SocketOnServer_2.getOutputStream()); Server_2in=new DataInputStream(SocketOnServer_2.getInputStream()); String Ciphertext=Server_2in.readUTF();//密文 byte[] data= Base64.getDecoder().decode(Ciphertext); String expr= AES.DecryptionAES(data); System.out.println("服务器接收到表达式:"+expr); int result; MyBC mybc=new MyBC(); result=mybc.evaluate(expr); Server_2out.writeUTF("后缀表达式:"+expr+",运算结果为:"+result); Thread.sleep(500); } catch (Exception e2) { System.out.println("客户端已断开"+e2); } } }
- 实验截图
任务四:密钥分发结对编程:1人负责客户端,一人负责服务器
- 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
DH算法相关链接:
- DH算法原理
-
密钥交换算法DH(Java实现)
- 密钥交换实现过程:
- 由消息发送的一方构建密钥,这里由甲方构建密钥。
- 由构建密钥的一方向对方公布其公钥,这里由甲方向乙方发布公钥。
- 由消息接收的一方通过对方公钥构建自身密钥,这里由乙方使用甲方公钥构建乙方密钥。
- 由消息接收的一方向对方公布其公钥,这里由乙方向甲方公布公钥。
-
使用密钥协定创建共享密钥截图:
-
使用创建的共享密钥就可以对AES的密钥进行加解密。因为生成的共享密钥为DESede密钥类型,则使用DESede加解密模式,密钥为共享密钥其余不变。即将共享密钥保存于文件Key_DESede_DH.dat中即可。
-
实现关键代码:
SecretKeySpec k=new SecretKeySpec(sb,"DESede"); FileOutputStream f=new FileOutputStream("A_Key_DESede_DH.dat");//指定产生密钥输出流文件 ObjectOutputStream b=new ObjectOutputStream(f);//将对象序列化,以流的方式进行处理 b.writeObject(k);//通过以对象序列化方式将密钥保存在文件中
任务五:完整性校验结对编程:1人负责客户端,一人负责服务器
- 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
- 实验截图:
遇到问题及解决方案
-
问题1:无法进行测试
- 问题2解决方案:该异常在客户端和服务器端均有可能发生,引起该异常的原因有两个,第一个就是如果一端的Socket被关闭(或主动关闭或者因为异常退出而引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer)。另一个是一端退出,但退出时并未关闭该连接,另一端如果在从连接中读数据则抛出该异常(Connection reset)。简单的说就是在连接断开后的读和写操作引起的。 在读取信息时不应该关闭客户端。
PSP
步骤 | 耗时(h) | 百分比 |
设计 | 2 | 20% |
代码实现 | 5 | 50% |
测试 | 2 | 20% |
分析总结 | 1 | 10% |
实验小结
本次实验过程中出现了很多问题,反反复复折腾了几天,不过在不断地从书中网上查询解决问题的过程中,收获颇丰。其实不管是学习生活还是今后工作,有时候需要适当地逼一下自己,可能在某一方面投入90%精力但仍不见希望时,我们需要做的可能就是再努力10%。这应该是本学期最后一次实验了,虽然Java实验真的磨人,不过不论从知识的提升还是耐力的磨练,总算是有所收获,还是挺感谢娄老师。希望自己以后也能屏住最后一口气,撑住往前走。