实验报告封面
课程:Java程序设计 班级:1751班 姓名:彭宇辰 学号:20175120
指导教师:娄嘉鹏 实验日期:2019年5月26日
实验时间:13:10 - 15:25 实验序号:20
实验名称:网络编程与安全
实验内容
任务一
-
两人一组结对编程:
-
0. 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA
-
1. 结对实现中缀表达式转后缀表达式的功能 MyBC.java
-
2. 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
-
3. 上传测试代码运行结果截图和码云链接
实验原理
栈的一个应用是用来对四则运算表达式进行求值。
表达式Exp = S1 + OP + S2
(S1 ,S2是两个操作数,OP为运算符)有三种标识方法:
- OP + S1 + S2 为前缀表示法
- S1 + OP + S2 为中缀表示法
- S1 + S2 + OP 为后缀表示法
例如:Exp = a * b + (c - d / e) * f
- 前缀式: + * a b * - c / d e f
- 中缀式: a * b + c - d / e * f
- 后缀式: a b * c d e / - f * +
可知:后缀式的运算规则为:运算符在式中出现的顺序恰为表达式的运算顺序;每个运算符和在它之前出现且紧靠它的两个操作数构成一个最小表达式。
中缀转后缀
对逆波兰式求值时,不需要再考虑运算符的优先级,只需从左到右扫描一遍后缀表达式即可。求值伪代码如下:
- 设置一个操作数栈,开始栈为空;
- 从左到右扫描后缀表达式,遇操作数,进栈;
- 若遇运算符,则从栈中退出两个元素,先退出的放到运算符的右边,后退出的放到运算符左边,运算后的结果再进栈,直到后缀表达式扫描完毕。
此时,栈中仅有一个元素,即为运算的结果。
实验代码
//MyDC.java
import com.sun.xml.internal.fastinfoset.util.CharArray; import java.util.StringTokenizer; import java.util.Stack; public class MyDC { /** * 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 */ private Stack<Integer> stack; public MyDC() { stack = new Stack<Integer>(); } public int evaluate(String expr) { int op1, op2, result = 0; String token; StringTokenizer tokenizer = new StringTokenizer(expr); while (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken(); //如果是运算符,调用isOperator if (isOperator(token)==true) { op2=stack.pop(); op1=stack.pop(); //从栈中弹出操作数2 //从栈中弹出操作数1 result=evalSingleOp(token.charAt(0),op1,op2); //根据运算符和两个操作数调用evalSingleOp计算result; stack.push(result); //计算result入栈; } else//如果是操作数 { stack.push(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; } }
//MyBC.java import java.util.*; /** * @author 20175120pyc */ 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; } }
//MyDCTest.java import java.util.*; public class MyDCTest { 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. 上传测试结果截图和码云链接
实验步骤
- 查看自己电脑的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()); } }
- 与20175221曾祥杰结对,我负责服务器端的测试
//Server1.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); } } }
后面我在自己的电脑上测试了客户端
//Client1.java import java.io.*; import java.net.*; public class Client1 { public static void main(String args[]) { System.out.println("20175221 正在启动...");//向服务器端发送信息 Socket mysocket;//创建客户端Socket DataInputStream in = null; DataOutputStream out = null; try { mysocket = new Socket("192.168.56.1", 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);//输出异常 } } }
实验截图
任务三
- 加密结对编程:1人负责客户端,一人负责服务器
- 0. 注意责任归宿,要会通过测试证明自己没有问题
- 1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
- 3. 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 4. 客户端显示服务器发送过来的结果
- 5. 上传测试结果截图和码云链接
实验原理
- 对称密钥的生成和保存
本实例给出Java中创建对称密钥的步骤,并通过对象序列化方式保存在文件中。
- 编程思路:
(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);
可以参考Java密码学
DES生成密钥
import javax.crypto.*; public class Skey_DES{ public static void main(String args[]) throws Exception{ KeyGenerator kg=KeyGenerator.getInstance("DESede"); kg.init(168); SecretKey k=kg.generateKey( ); FileOutputStream f=new FileOutputStream("key1.dat"); ObjectOutputStream b=new ObjectOutputStream(f); b.writeObject(k); } }
打印密钥
import java.io.*; import java.security.*; public class Skey_kb{ public static void main(String args[]) throws Exception{ FileInputStream f=new FileInputStream("key1.dat"); ObjectInputStream b=new ObjectInputStream(f); Key k=(Key)b.readObject( ); byte[ ] kb=k.getEncoded( ); FileOutputStream f2=new FileOutputStream("keykb1.dat"); f2.write(kb); // 打印密钥编码中的内容 for(int i=0;i<kb.length;i++){ System.out.print(kb[i]+","); } } }
DES加密
import java.io.*; import java.security.*; import javax.crypto.*; public class SEnc{ public static void main(String args[]) throws Exception{ String s="Hello World!"; FileInputStream f=new FileInputStream("key1.dat"); ObjectInputStream b=new ObjectInputStream(f); Key k=(Key)b.readObject( ); Cipher cp=Cipher.getInstance("DESede"); cp.init(Cipher.ENCRYPT_MODE, k); byte ptext[]=s.getBytes("UTF8"); for(int i=0;i<ptext.length;i++){ System.out.print(ptext[i]+","); } System.out.println(""); byte ctext[]=cp.doFinal(ptext); for(int i=0;i<ctext.length;i++){ System.out.print(ctext[i] +","); } FileOutputStream f2=new FileOutputStream("SEnc.dat"); f2.write(ctext); } }
DES解密
import java.io.*; import java.security.*; import javax.crypto.*; import javax.crypto.spec.*; public class SDec{ public static void main(String args[]) throws Exception{ // 获取密文 FileInputStream f=new FileInputStream("SEnc.dat"); int num=f.available(); byte[ ] ctext=new byte[num]; f.read(ctext); // 获取密钥 FileInputStream f2=new FileInputStream("keykb1.dat"); int num2=f2.available(); byte[ ] keykb=new byte[num2]; f2.read(keykb); SecretKeySpec k=new SecretKeySpec(keykb,"DESede"); // 解密 Cipher cp=Cipher.getInstance("DESede"); cp.init(Cipher.DECRYPT_MODE, k); byte []ptext=cp.doFinal(ctext); // 显示明文 String p=new String(ptext,"UTF8"); System.out.println(p); } }
我负责服务器端测试
//Server2.java import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class Server2 { 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); } } }
后面做了Client测试
//Client2.java import javax.crypto.Cipher; import java.io.*; import java.net.Socket; import java.security.Key; import java.util.Scanner; public class Client2 { 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("192.168.56.1", 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); } } }
任务四
使用密钥协定创建共享密钥
- 密钥分发结对编程:1人负责客户端,一人负责服务器
- 0. 注意责任归宿,要会通过测试证明自己没有问题
- 1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
- 3. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 4. 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 5. 客户端显示服务器发送过来的结果
- 6. 上传测试结果截图和码云链接
实验步骤
- 创建DH公钥和私钥
DH算法是建立在DH公钥和私钥的基础上的, A需要和B共享密钥时,A和B各自生成DH公钥和私钥,公钥对外公布而私钥各自秘密保存。本实例将介绍Java中如何创建并部署DH公钥和私钥,以便后面一小节利用它创建共享密钥。
import java.io.*; import java.math.*; import java.security.*; import java.security.spec.*; import javax.crypto.*; import javax.crypto.spec.*; import javax.crypto.interfaces.*; public class Key_DH{ //三个静态变量的定义从 // C:j2sdk-1_4_0-docdocsguidesecurityjceJCERefGuide.html // 拷贝而来 // The 1024 bit Diffie-Hellman modulus values used by SKIP private static final byte skip1024ModulusBytes[] = { (byte)0xF4, (byte)0x88, (byte)0xFD, (byte)0x58, (byte)0x4E, (byte)0x49, (byte)0xDB, (byte)0xCD, (byte)0x20, (byte)0xB4, (byte)0x9D, (byte)0xE4, (byte)0x91, (byte)0x07, (byte)0x36, (byte)0x6B, (byte)0x33, (byte)0x6C, (byte)0x38, (byte)0x0D, (byte)0x45, (byte)0x1D, (byte)0x0F, (byte)0x7C, (byte)0x88, (byte)0xB3, (byte)0x1C, (byte)0x7C, (byte)0x5B, (byte)0x2D, (byte)0x8E, (byte)0xF6, (byte)0xF3, (byte)0xC9, (byte)0x23, (byte)0xC0, (byte)0x43, (byte)0xF0, (byte)0xA5, (byte)0x5B, (byte)0x18, (byte)0x8D, (byte)0x8E, (byte)0xBB, (byte)0x55, (byte)0x8C, (byte)0xB8, (byte)0x5D, (byte)0x38, (byte)0xD3, (byte)0x34, (byte)0xFD, (byte)0x7C, (byte)0x17, (byte)0x57, (byte)0x43, (byte)0xA3, (byte)0x1D, (byte)0x18, (byte)0x6C, (byte)0xDE, (byte)0x33, (byte)0x21, (byte)0x2C, (byte)0xB5, (byte)0x2A, (byte)0xFF, (byte)0x3C, (byte)0xE1, (byte)0xB1, (byte)0x29, (byte)0x40, (byte)0x18, (byte)0x11, (byte)0x8D, (byte)0x7C, (byte)0x84, (byte)0xA7, (byte)0x0A, (byte)0x72, (byte)0xD6, (byte)0x86, (byte)0xC4, (byte)0x03, (byte)0x19, (byte)0xC8, (byte)0x07, (byte)0x29, (byte)0x7A, (byte)0xCA, (byte)0x95, (byte)0x0C, (byte)0xD9, (byte)0x96, (byte)0x9F, (byte)0xAB, (byte)0xD0, (byte)0x0A, (byte)0x50, (byte)0x9B, (byte)0x02, (byte)0x46, (byte)0xD3, (byte)0x08, (byte)0x3D, (byte)0x66, (byte)0xA4, (byte)0x5D, (byte)0x41, (byte)0x9F, (byte)0x9C, (byte)0x7C, (byte)0xBD, (byte)0x89, (byte)0x4B, (byte)0x22, (byte)0x19, (byte)0x26, (byte)0xBA, (byte)0xAB, (byte)0xA2, (byte)0x5E, (byte)0xC3, (byte)0x55, (byte)0xE9, (byte)0x2F, (byte)0x78, (byte)0xC7 }; // The SKIP 1024 bit modulus private static final BigInteger skip1024Modulus = new BigInteger(1, skip1024ModulusBytes); // The base used with the SKIP 1024 bit modulus private static final BigInteger skip1024Base = BigInteger.valueOf(2); public static void DH(String s1,String s2) throws Exception{ DHParameterSpec DHP= new DHParameterSpec(skip1024Modulus,skip1024Base); KeyPairGenerator kpg= KeyPairGenerator.getInstance("DH"); kpg.initialize(DHP); KeyPair kp=kpg.genKeyPair(); PublicKey pbk=kp.getPublic(); PrivateKey prk=kp.getPrivate(); // 保存公钥 FileOutputStream f1=new FileOutputStream(s1); ObjectOutputStream b1=new ObjectOutputStream(f1); b1.writeObject(pbk); // 保存私钥 FileOutputStream f2=new FileOutputStream(s2); ObjectOutputStream b2=new ObjectOutputStream(f2); b2.writeObject(prk); } }
- 创建共享密钥
DH算法中,A可以用自己的密钥和B的公钥按照一定方法生成一个密钥,B也可以用自己的密钥和A的公钥按照一定方法生成一个密钥,由于一些数学规律,这两个密钥完全相同。这样,A和B间就有了一个共同的密钥可以用于各种加密。本实例介绍Java中在上一小节的基础上如何利用DH公钥和私钥各自创建共享密钥。
编程思路:
Java中KeyAgreement类实现了密钥协定,它使用init( )方法传入自己的私钥,使用doPhase( )方法传入对方的公钥,进而可以使用generateSecret( )方法生成共享的信息具体步骤如下:
(1) 读取自己的DH私钥和对方的DH公钥
FileInputStream f1=new FileInputStream(args[0]); ObjectInputStream b1=new ObjectInputStream(f1); PublicKey pbk=(PublicKey)b1.readObject( ); FileInputStream f2=new FileInputStream(args[1]); ObjectInputStream b2=new ObjectInputStream(f2); PrivateKey prk=(PrivateKey)b2.readObject( );
(2) 创建密钥协定对象
KeyAgreement ka=KeyAgreement.getInstance("DH");
(3) 初始化密钥协定对象
ka.init(prk);
(4) 执行密钥协定
ka.doPhase(pbk,true);
(5) 生成共享信息
byte[ ] sb=ka.generateSecret();
//KeyAgree.java
import javax.crypto.KeyAgreement; import javax.crypto.spec.SecretKeySpec; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.security.PrivateKey; import java.security.PublicKey; public class KeyAgree{ public static void DH(String s1,String s2) throws Exception{ // 读取对方的DH公钥 FileInputStream f1=new FileInputStream(s1); ObjectInputStream b1=new ObjectInputStream(f1); PublicKey pbk=(PublicKey)b1.readObject( ); //读取自己的DH私钥 FileInputStream f2=new FileInputStream(s2); ObjectInputStream b2=new ObjectInputStream(f2); PrivateKey prk=(PrivateKey)b2.readObject( ); // 执行密钥协定 KeyAgreement ka=KeyAgreement.getInstance("DH"); ka.init(prk); ka.doPhase(pbk,true); //生成共享信息 byte[ ] sb=ka.generateSecret(); FileOutputStream fsb = new FileOutputStream("sb.dat"); fsb.write(sb); } }
服务器端测试
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 Server3 { 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); } } }
客户端测试
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 Client3 { 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("192.168.56.1", 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); } } }
任务五
Java摘要算法- MD5
- 完整性校验结对编程:1人负责客户端,一人负责服务器
- 0. 注意责任归宿,要会通过测试证明自己没有问题
- 1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
- 3. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 4. 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 5. 客户端显示服务器发送过来的结果
- 6. 上传测试结果截图和码云链接
实验原理
使用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转换为字符串。
import java.security.*; public class DigestPass{ public static String DP(String s1) throws Exception{ String x=s1; MessageDigest m=MessageDigest.getInstance("MD5"); m.update(x.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); } return result; } }
服务器端测试
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 Server4 { 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("等待客户呼叫"); socketOnServer = serverForClient.accept(); //堵塞状态,除非有客户呼叫 System.out.println("客户已连接"); 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 pMd5 = DigestPass.DP(p); System.out.println("被解密明文的MD5值:"+pMd5); if (pMd5.equals(check)){ System.out.println("和客户端的MD5值一致"); System.out.println("计算后缀表达式" + p); out.writeUTF(mydc.evaluate(p)+""); } else { System.out.println("警告:和客户端的MD5值不一致!"); } } catch (Exception e) { System.out.println("客户已断开" + e); } } }
客户端测试
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 Client4 { 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("192.168.56.1", 5221); System.out.println("客户端启动..."); 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(500); 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("客户收到服务器的回应:" + s); } catch (Exception e) { System.out.println("服务器已断开" + e); } } }
实验体会
这次实验内容较多,不过许多知识点娄老师都在课上讲过,我们也练习过,所以没有那么难。
这次实验的难点主要在main方法的编写。如何调用加解密算法,如何用Java Socket建立传输等等。但这次结对编程减少了任务量,我也体会到了结对的好处。
这是最后一次实验,在这学期的Java实验中我深刻理解了“做中学”的含义,实践出真知,面对问题时才能真正使用知识。