实验五 网络编程与安全
目录
一、实验报告封面
北京电子科技学院(BESTI)
实 验 报 告
◆ | ◇ | ◆ | ◇ |
---|---|---|---|
课程 | Java程序设计 | 班级 | 1652班 |
姓名 | 蔡霓 | 学号 | 20165223 |
成绩 | 指导教师 | 娄嘉鹏 | |
实验日期 | 2018年5月28日 | 实验密级 | 非密级 |
预习程度 | 已预习 | 实验时间 | 13:45 - 15:25 |
必修/选修 | 选修 | 实验序号 | 五 |
实验名称:Android开发基础
二、具体实验内容
(一)网络编程与安全-1
两人一组结对编程:
0.参考
1.结对实现中缀表达式转后缀表达式的功能 MyBC.java
2.结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
3.上传测试代码运行结果截图和码云链接
(1)实验步骤
- 编写实现中缀表达式转后缀表达式的功能代码
MyBC.java
- 编写实现后缀表达式求值功能的代码
MyDC.java
- 编写测试代码
MyDCTester.java
及MyBCTest.java
(2)实验代码
详见码云链接:
MyBC.java
import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.stream.Collectors;import java.lang.String;import org.junit.Test;public class MyBC{ private static final Mapbasic = new HashMap (); static { basic.put('-', 1); basic.put('+', 1); basic.put('*', 2); basic.put('/', 2); basic.put('(', 0); } //中缀表达式 转 后缀表达式 public static String toSuffix(String infix){ List queue = new ArrayList (); List stack = new ArrayList (); char[] charArr = infix.trim().toCharArray(); String standard = "*/+-()"; char ch = '&'; int len = 0; for (int i = 0; i < charArr.length; i++) { ch = charArr[i]; if(Character.isDigit(ch)) { len++; }else if(Character.isLetter(ch)) { len++; }else if(ch == '.'){ len++; }else if(Character.isSpaceChar(ch)) { if(len > 0) { queue.add(String.valueOf(Arrays.copyOfRange(charArr, i - len, i))); len = 0; } continue; }else if(standard.indexOf(ch) != -1) { if(len > 0) { queue.add(String.valueOf(Arrays.copyOfRange(charArr, i - len, i))); len = 0; } if(ch == '(') { stack.add(ch); continue; } if (!stack.isEmpty()) { int size = stack.size() - 1; boolean flag = false; while (size >= 0 && ch == ')' && stack.get(size) != '(') { queue.add(String.valueOf(stack.remove(size))); size--; flag = true; } while (size >= 0 && !flag && basic.get(stack.get(size)) >= basic.get(ch)) { queue.add(String.valueOf(stack.remove(size))); size--; } } if(ch != ')') { stack.add(ch); } else { stack.remove(stack.size() - 1); } } if(i == charArr.length - 1) { if(len > 0) { queue.add(String.valueOf(Arrays.copyOfRange(charArr, i - len+1, i+1))); } int size = stack.size() - 1; while (size >= 0) { queue.add(String.valueOf(stack.remove(size))); size--; } } } return queue.stream().collect(Collectors.joining(" ")); }}
MyDC.java
import javax.xml.bind.annotation.XmlType;import java.lang.Integer;import java.util.StringTokenizer;import java.util.Stack;public class MyDC{ /** constant for addition symbol */ private final String ADD="+"; /** constant for subtraction symbol */ private final String SUBTRACT="-"; /** constant for multiplication symbol */ private final String MULTIPLY="*"; /** constant for division symbol */ private final String DIVIDE="/"; /** the stack */ private Stackstack; public MyDC(){ stack=new Stack (); } 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)) { //从栈中弹出操作数2 op2 = stack.pop(); //从栈中弹出操作数1 op1 = stack.pop(); //根据运算符和两个操作数调用evalSingleOp计算result; result = evalSingleOp(token, op1, op2); //计算result入栈; stack.push(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(String 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; default: break; } return result; }}
(3)实验截图
(二)网络编程与安全-2
结对编程:1人负责客户端,一人负责服务器
0.注意责任归宿,要会通过测试证明自己没有问题
1.基于Java Socket实现客户端/服务器功能,传输方式用TCP
2.客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
3.服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
4.客户端显示服务器发送过来的结果
5.上传测试结果截图和码云链接
(1)实验步骤
- 编写服务器端与客户端链接代码
- 客户端输入中缀表达式,调用
MyBC.java
转为后缀表达式 - 服务器接收后缀表达式,调用
MyDC.java
计算结果值并返回到客户端
(2)实验代码
详见码云链接:
- 服务器端:
Service.java
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;public class Service { public static void main(String[] args) throws IOException{ Service socketService = new Service(); socketService.oneServer(); } public void oneServer(){ try{ ServerSocket server=null; try{ server=new ServerSocket(5223); //客户端链接端口 System.out.println("服务器启动成功!"); }catch(Exception e) { System.out.println("没有启动监听!"+e); } Socket socket=null; try{ socket=server.accept(); }catch(Exception e) { System.out.println("Error."+e); } String line,line2; BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter writer=new PrintWriter(socket.getOutputStream()); BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); line2=in.readLine(); System.out.println("客户端:"+line2); MyDC f = new MyDC(); System.out.printf("%d",f.evaluate(line2)); writer.println(Integer.toString(f.evaluate(line2))); line=br.readLine(); while(!line.equals("end")){ writer.println(line); writer.flush(); System.out.println("服务器:"+Integer.toString(f.evaluate(in.readLine()))); System.out.println("客户端:"+in.readLine()); line=br.readLine(); } writer.close(); in.close(); socket.close(); server.close(); }catch(Exception e) { System.out.println("Error."+e); } }}
- 客户端:
Client.java
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.InetAddress;import java.net.Socket;import java.net.URL;public class Client { public static void main(String[] args) throws IOException { try { //服务器的IP地址与所选端口(IP地址通过命令行指令ipconfig查出) Socket socket = new Socket("10.1.1.234", 5223); System.out.println("客户端启动成功!"); System.out.println("请输入中缀表达式:"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); PrintWriter write = new PrintWriter(socket.getOutputStream()); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); String expression; MyBC bc = new MyBC(); expression = br.readLine(); String input =new String(); input = bc.toSuffix("1+(4*2)/2+5+4*3-4"); while (!expression.equals("end")) { write.println(input); write.println(expression); write.flush(); System.out.println("转化的后缀表达式为:" + input); System.out.println("服务器返回值为:" + in.readLine()); expression = br.readLine(); } write.close(); in.close(); socket.close(); } catch (Exception e) { System.out.println("无法监听:" + e); } }}
(3)实验截图
(三)网络编程与安全-3
加密结对编程:1人负责客户端,一人负责服务器
0.注意责任归宿,要会通过测试证明自己没有问题
1.基于Java Socket实现客户端/服务器功能,传输方式用TCP
2.客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
3.服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
4.客户端显示服务器发送过来的结果
5.上传测试结果截图和码云链接
(1)实验步骤
- 前期基本步骤同上一提交点
- 编写加解密代码
AES.JAVA
- 在客户端输入中缀表达式,调用
MyBC.java
转后缀表达式,再调用AES.JAVA
的加密部分转加密密文 - 服务器接收到密文,调用
AES.JAVA
的解密部分对密文解密,再调用MyDC.java
对解密明文计算值,返回客户端
(2)实验代码
详见码云链接:
AES.java
import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import sun.misc.BASE64Decoder;import sun.misc.BASE64Encoder;public class AES { //初始向量 public static final String VIPARA = "aabbccddeeffgghh"; //编码方式 public static final String bm = "UTF-8"; //私钥 private static final String ASE_KEY="aabbccddeeffgghh"; /** * 加密 */ public static String encrypt(String cleartext) { try { IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes()); SecretKeySpec key = new SecretKeySpec(ASE_KEY.getBytes(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv); byte[] encryptedData = cipher.doFinal(cleartext.getBytes(bm)); return new BASE64Encoder().encode(encryptedData); } catch (Exception e) { e.printStackTrace(); return ""; } } /** * 解密 */ public static String decrypt(String encrypted) { try { byte[] byteMi = new BASE64Decoder().decodeBuffer(encrypted); IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes()); SecretKeySpec key = new SecretKeySpec( ASE_KEY.getBytes(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, key, zeroIv); byte[] decryptedData = cipher.doFinal(byteMi); return new String(decryptedData, bm); } catch (Exception e) { e.printStackTrace(); return ""; } }}
- 服务器端新增部分:
Service2.java
...... String line,line2; BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter writer=new PrintWriter(socket.getOutputStream()); BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); line2=in.readLine(); System.out.println("客户端:"+line2); //创建对象aes,并调用 AES 类中的解密方法 AES aes = new AES(); String line3 = new String(); line3 = aes.decrypt(line2); //调用 MyDC 类计算值 MyDC f = new MyDC(); System.out.printf("%d",f.evaluate(line3)); writer.println(Integer.toString(f.evaluate(line3))); line=br.readLine(); ......
- 客户端新增部分:
Client2.java
...... MyBC bc = new MyBC(); expression = br.readLine(); String input =new String(); String input2 = new String(); input = bc.toSuffix("1+(4*2)/2+5+4*3-4"); //创建对象aes,并调用 AES 类中的加密方法 AES aes = new AES(); input2 = aes.encrypt(input); while (!expression.equals("end")) { write.println(input2); write.println(expression); write.flush(); System.out.println("aes加密的后缀表达式为:" + input2); System.out.println("服务器返回值为:" + in.readLine()); expression = br.readLine(); } ......
(3)实验截图
(四)网络编程与安全-4
密钥分发结对编程:1人负责客户端,一人负责服务器
0.注意责任归宿,要会通过测试证明自己没有问题
1.基于Java Socket实现客户端/服务器功能,传输方式用TCP
2.客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
3.客户端和服务器用DH算法进行3DES或AES算法的密钥交换
4.服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
5.客户端显示服务器发送过来的结果
6.上传测试结果截图和码云链接
(1)实验步骤
- 前期操作与前一提交点一致
- 在实验提交点3的基础上再添加密钥交换算法
DHCode.java
- 客户端和服务器用DH算法进行AES算法的密钥交换
(2)实验代码
详见码云链接:
- 生成密钥对:
HQKeyPair.java
import java.security.KeyPair;/** * 生成密钥对 */public class HQKeyPair{ private byte[] privateKey; private byte[] publicKey; public HQKeyPair() { } public HQKeyPair(KeyPair keyPair) { this.privateKey = keyPair.getPrivate().getEncoded(); this.publicKey = keyPair.getPublic().getEncoded(); } public HQKeyPair(byte[] privateKey, byte[] publicKey) { this.privateKey = privateKey; this.publicKey = publicKey; } public byte[] getPrivateKey() { return privateKey; } public void setPrivateKey(byte[] privateKey) { this.privateKey = privateKey; } public byte[] getPublicKey() { return publicKey; } public void setPublicKey(byte[] publicKey) { this.publicKey = publicKey; }}
- DH算法实现:
DHCode.java
import java.security.Key;import java.security.KeyFactory;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.PrivateKey;import java.security.PublicKey;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import java.util.HashMap;import java.util.Map;import javax.crypto.Cipher;import javax.crypto.KeyAgreement;import javax.crypto.SecretKey;import javax.crypto.interfaces.DHPrivateKey;import javax.crypto.interfaces.DHPublicKey;import javax.crypto.spec.DHParameterSpec;import javax.crypto.spec.SecretKeySpec;public abstract class DHCoder { /** * 非对称加密密钥算法 */ private static final String KEY_ALGORITHM = "DH"; /** * 选择AES算法 */ private static final String SELECT_ALGORITHM = "AES"; /** * 密钥长度 */ private static final int KEY_SIZE = 512; //公钥 private static final String PUBLIC_KEY = "DHPublicKey"; //私钥 private static final String PRIVATE_KEY = "DHPrivateKey"; /** * 初始化甲方密钥 */ public static MapinitKey() throws Exception{ //实例化密钥对生成器 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM); //初始化密钥对生成器 keyPairGenerator.initialize(KEY_SIZE); //生成密钥对 KeyPair keyPair = keyPairGenerator.generateKeyPair(); //甲方公钥 DHPublicKey publicKey = (DHPublicKey)keyPair.getPublic(); //甲方私钥 DHPrivateKey privateKey = (DHPrivateKey)keyPair.getPrivate(); //将密钥对存储在Map中 Map keyMap = new HashMap (2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** * 初始化乙方密钥 */ public static Map initKey(byte[] key) throws Exception{ //解析甲方公钥 //转换公钥材料 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key); //实例化密钥工厂 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); //产生公钥 PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); //由甲方公钥构建乙方密钥 DHParameterSpec dhParameterSpec = ((DHPublicKey)pubKey).getParams(); //实例化密钥对生成器 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM); //初始化密钥对生成器 keyPairGenerator.initialize(KEY_SIZE); //产生密钥对 KeyPair keyPair = keyPairGenerator.generateKeyPair(); //乙方公钥 DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic(); //乙方私约 DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate(); //将密钥对存储在Map中 Map keyMap = new HashMap (2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** * 加密 */ public static byte[] encrypt(byte[] data, byte[] key) throws Exception{ //生成本地密钥 SecretKey secretKey = new SecretKeySpec(key, SELECT_ALGORITHM); //数据加密 Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(data); } /** * 解密 */ public static byte[] decrypt(byte[] data, byte[] key) throws Exception{ //生成本地密钥 SecretKey secretKey = new SecretKeySpec(key, SELECT_ALGORITHM); //数据揭秘 Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(data); } /** * 构建密钥 */ public static byte[] getSecretKey(byte[] publicKey, byte[] privateKey) throws Exception{ //实例化密钥工厂 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); //初始化公钥 //密钥材料转换 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKey); //产生公钥 PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); //初始化私钥 //密钥材料转换 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey); //产生私钥 PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); //实例化 KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm()); //初始化 keyAgree.init(priKey); keyAgree.doPhase(pubKey, true); //生成本地密钥 SecretKey secretKey = keyAgree.generateSecret(SELECT_ALGORITHM); return secretKey.getEncoded(); } /** * 私钥 */ public static byte[] getPrivateKey(Map keyMap) throws Exception{ Key key = (Key) keyMap.get(PRIVATE_KEY); return key.getEncoded(); } /** * 公钥 */ public static byte[] getPublicKey(Map keyMap) throws Exception{ Key key = (Key) keyMap.get(PUBLIC_KEY); return key.getEncoded(); }}
(3)实验截图
(五)网络编程与安全-5
完整性校验结对编程:1人负责客户端,一人负责服务器
0.注意责任归宿,要会通过测试证明自己没有问题
1.基于Java Socket实现客户端/服务器功能,传输方式用TCP
2.客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
3.客户端和服务器用DH算法进行3DES或AES算法的密钥交换
4.服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
5.客户端显示服务器发送过来的结果
6.上传测试结果截图和码云链接
(1)实验步骤
- 前期操作与前一提交点一致
- 客户端用AES算法加密后缀表达式,把密文和明文的MD5値发送给服务器
- 客户端和服务器用DH算法进行AES算法的密钥交换
- 服务器解密后,计算明文的MD5值,与客户端的MD5值对比
(2)实验代码
详见码云链接:
MD5.java
import java.security.MessageDigest;/** * 求MD5值 */public class MD5 { public final static String[] hashValueString = {"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"}; public static String byteToString(byte[] hashValueBytes){ String result = ""; for(int i=0;i0 ? hashValueInt : hashValueInt+256; int high = hashValueInt / 16; //byte 的高4位 int low = hashValueInt % 16; //byte 的低4位 result += hashValueString[high] + hashValueString[low]; } return result; } public static void byteToNum(byte[] hashValueBytes){ } public static String GetMd5Code(String str) throws Exception { String md5Value = ""; MessageDigest md = MessageDigest.getInstance("MD5"); byte[] hashValueBytes = md.digest(str.getBytes()); //返回值为存放hash 值的byte 数组 md5Value = byteToString(hashValueBytes); return md5Value; }}
(3)实验截图
三、实验总结
遇到的问题
在实验2~5点中,使用Socket实现客户端/服务器功能时需要提供服务器端的IP地址,查询资料后了解到有两种查看方式,这里使用了命令行输入指令ipconfig
查询IPv4地址。也可以在电脑的“网络和共享中心”→“WLAN连接”→“详细信息”中查询IPv4地址。
法一:
法二:
参考资料
四、PSP时间
实验步骤 | 耗时 | 百分比 |
---|---|---|
题目分析 | 1h | 10% |
思路设计 | 2h | 20% |
代码实现 | 5h | 50% |
调试测试 | 1h | 10% |
实验总结 | 1h | 10% |