实验五 网络编程与安全 实验报告 20162305 李昱兴
实验一:结对实现中缀表达式转后缀表达式的功能 MyBC.java
实验要求
- 两人一组结对编程:
- 结对实现中缀表达式转后缀表达式的功能 MyBC.java
- 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
- 上传测试代码运行结果截图和码云链接
实现思路
- 利用栈去产生并计算一个后缀表达式,这是MyDC的功能。在MyBC中,先将中缀表达式转换成后缀表达式,再调用MyDC中evaluate方法进行计算。所以重点就落在了如何将后缀表达式转化成中缀表达式。在老师给的博客指导中,老师详细解释了如何实现这一功能,先判断算符的优先级,根据不同的优先级,将表达式中的各个元素同栈顶元素比较,根据不同的优先级决定元素是入栈还是出栈,并将相应的元素赋给数组Char中的不同元素,最后用toString转化类型。在测试类中,调用两个类的方法进行计算,最后打印出结果。
- 有关中缀后缀的学习指导
成果截图
(实验一截图)
实验二:结对编程,利用服务器和客户端实现中缀表达式的计算
实验要求
结对编程:1人负责客户端,一人负责服务器
注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
- 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
实现思路
- 结对编程实现这一功能,两个人一个人负责写服务器,另一个人负责写客户端。对于服务器,先定义一个监听的端口号,再创建ServerSocket对象绑定监听端口,用accept方法与客户端创建连接,若连接异常则弹出错误。接着在利用DataInputStream获取客户端输入的数据和内容,并调用MyDC中的evaluate方法处理数据进行计算,接着利用DataOutputStream将数据返回给客户端,并打印出来结果,如有异常则弹出错误。等到socket为空时,关闭服务器结束。
- 对于客户端,首先定义服务器地址和服务器端口号,服务器地址采用本机电脑IP地址,而服务器端口号同服务器所监听的端口号相同。接着,创建一个流套接字并将其连接到指定主机上的指定端口号,实现客户端和服务器的连接。然后,客户端读取数据并发送给服务器,接着,利用MyBC中的方法对数据进行计算,将结果输出,并获取服务器中利用DC得到的结果并返回出来。
成果截图
(实验二截图)
实验三:利用3DES或AES对表达式加密,获取密钥后运算产生结果
实验要求
- 加密结对编程:1人负责客户端,一人负责服务器
- 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
- 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
实现思路
- 首先运行Skey_DES.java,将在当前目录下生成key1.data保存产生的密钥。再运行Skey_kb.java,在当前目录下生成keykb1.dat保存上一步生成的密钥的编码。改写DES加密代码SEnc.java,新添一个String类型成员变量,新添一个接受字符串参数的构造方法,并把其主函数改成加密方法,方便在客户端代码中直接调用。运行客户端代码ClientSend.java,在其中新建一个SEnc类实例,并调用该实例的加密方法,参数为后缀表达式;把密文文件和密钥编码文件发送给客户端,最后接收客户端返回的结果。
- Skey_DES.java 与 Skey_kb.java代码参考Java密码学。
成果截图
(实验三截图)
实验四:利用DH算法进行3DES或AES算法的密钥交换
实验要求
- 密钥分发结对编程:1人负责客户端,一人负责服务器
- 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
实现方法
- 本次实验的关键部分就是DH算法,DH密钥交换算法的安全性基于有限域上的离散对数难题。基于这种安全性,通过DH算法进行密钥分配,使得消息的收发双方可以安全地交换一个密钥,再通过这个密钥对数据进行加密和解密处理。
- 我们以消息传递模型为例,甲方作为发送者,乙方作为接受者,分述甲乙双方如何构建密钥、交互密钥和加密数据。首先,甲乙双方需要在收发消息前构建自己的密钥对, 甲乙双方构建密钥需要经过以下几个步骤:
- 1)由消息发送的一方构建密钥,这里由甲方构建密钥。
- 2)由构建密钥的一方向对方公布其公钥,这里由甲方向乙方发布公钥。
- 3)由消息接收的一方通过对方公钥构建自身密钥,这里由乙方使用甲方公钥构建乙方密钥。
- 4)由消息接收的一方向对方公布其公钥,这里由乙方向甲方公布公钥。
- 这里要注意的是,乙方构建自己密钥对的时候需要使用甲方公钥作为参数这是很关键的一点,如果缺少了这一环节则无法确保甲乙双方获得同一个密钥,消息加密更无从谈起。其次,假设甲乙双方事先约定好了用于数据加密的对称加密算法(如AES算法),并构建本地密钥(即对称加密算法中的密钥)。甲方需要使用自己的私钥和乙方的公钥才能构建自己的本地密钥,乙方需要使用自己的私钥和甲方的公钥才能构建自己的本地密钥。
虽然甲乙双方使用了不同的密钥来构建本地密钥,但是甲乙两方得到的密钥其实是一致的,也正是基于此,甲乙双方才能顺利地进行加密消息的传送。最后,甲乙双方构建了本地密钥后,可按照基于对称加密算法的消息传递模型完成消息传递。 - 这部分内容我也是从别的博客上学习来的,有些地方我也不是特别的清楚,不过大体上也能掌握。其他的详细内容,我推荐给大家看这篇博客
- 密钥交换算法DH(Java实现)
成果截图
- (实验四截图)
实验五:用3DES或AES算法加密通过网络把密文和明文的MD5值发送给服务器
实验要求
- 完整性校验结对编程:1人负责客户端,一人负责服务器
- 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
实现方法
- 客户端代码
/**
* Created by hp on 2017/6/8.
**/
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.Socket;
import java.security.*;
public class Client {
public static final String IP_ADDR = "localhost" ;
public static final int PORT = 12345;//服务器端口号
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println("客户端启动...");
System.out.println("当接收到服务器端字符为 \"STOP\" 的时候, 客户端将终止\n");
while (true) {
Socket socket = null;
try {
//创建一个流套接字并将其连接到指定主机上的指定端口号
socket = new Socket(IP_ADDR, PORT);
//读取服务器端数据
DataInputStream input = new DataInputStream(socket.getInputStream());
//向服务器端发送数据
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
System.out.print("请输入: \t");
String str = new BufferedReader(new InputStreamReader(System.in)).readLine();
MyBC turner = new MyBC();
String str1 = turner.turn(str);
int length=0,i=0;
while(str1.charAt(i)!='\0'){
length++;
i++;
}
String str2 = str1.substring(1,length-1);
// 读取对方的DH公钥
FileInputStream f1=new FileInputStream("pubKeyC.dat");
ObjectInputStream b1=new ObjectInputStream(f1);
PublicKey pbk=(PublicKey)b1.readObject( );
//读取自己的DH私钥
FileInputStream f2=new FileInputStream("priKeyS.dat");
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();
String exp = Crypt.parseByte2HexStr(Crypt.encrypt(str2,parseByte2HexStr(sb)));
String x= str2;
MessageDigest m=MessageDigest.getInstance("MD5");
m.update(x.getBytes("UTF8"));
byte s[ ]=m.digest( );
String result="";
for (int j=0; j<s.length; j++){
result+=Integer.toHexString((0x000000ff & s[j]) |
0xffffff00).substring(6);
}
out.writeUTF(exp + "&&md5" + result);
String ret = input.readUTF();
System.out.println("服务器端返回过来的是: " + ret);
out.close();
input.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
private static class Crypt {
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(byteContent);
return result; // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
public static byte[] decrypt(byte[] content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(content);
return result;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length()/2];
for (int i = 0;i< hexStr.length()/2; i++) {
int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
}
- 服务器代码
/**
* Created by hp on 2017/6/8.
*/
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.*;
public class Server {
public static final int PORT = 12345;//监听的端口号
public static void main(String[] args) {
System.out.println("服务器启动...\n");
Server server = new Server();
server.init();
}
public void init() {
try {
ServerSocket serverSocket = new ServerSocket(PORT);
while (true) {
Socket client = serverSocket.accept();
new HandlerThread(client);
}
} catch (Exception e) {
System.out.println("服务器异常: " + e.getMessage());
}
}
private class HandlerThread implements Runnable {
private Socket socket;
public HandlerThread(Socket client) {
socket = client;
new Thread(this).start();
}
public String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length()/2];
for (int i = 0;i< hexStr.length()/2; i++) {
int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
public void run() {
try {
// 读取客户端数据
DataInputStream input = new DataInputStream(socket.getInputStream());
String clientInputStr = input.readUTF();//这里要注意和客户端输出流的写方法对应,否则会抛 EOFException
// 处理客户端数据
System.out.println("客户端发过来的内容:" + clientInputStr);
// 读取对方的DH公钥
FileInputStream f1=new FileInputStream("pubKeyC.dat");
ObjectInputStream b1=new ObjectInputStream(f1);
PublicKey pbk=(PublicKey)b1.readObject( );
//读取自己的DH私钥
FileInputStream f2=new FileInputStream("priKeyS.dat");
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();
String md5 = clientInputStr.split("&&md5")[1];
clientInputStr = clientInputStr.split("&&md5")[0];
String exp = new String(Crypt.decrypt(Crypt.parseHexStr2Byte(clientInputStr),parseByte2HexStr(sb)));
System.out.println(exp);
String x= exp;
MessageDigest m=MessageDigest.getInstance("MD5");
m.update(x.getBytes("UTF8"));
byte s[ ]=m.digest( );
String res="";
for (int j=0; j<s.length; j++){
res +=Integer.toHexString((0x000000ff & s[j]) |
0xffffff00).substring(6);
}
System.out.printf("md5Check:" + md5.equals(res));
MyDC dc = new MyDC();
int result = dc.evaluate(exp);
// 向客户端回复信息
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
System.out.print("计算结果:\t" + result);
// 发送键盘输入的一行
out.writeUTF(String.valueOf(result));
out.close();
input.close();
} catch (Exception e) {
System.out.println("服务器 run 异常: " + e.getMessage());
} finally {
if (socket != null) {
try {
socket.close();
} catch (Exception e) {
socket = null;
System.out.println("服务端 finally 异常:" + e.getMessage());
}
}
}
}
}
private static class Crypt {
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(byteContent);
return result; // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
public static byte[] decrypt(byte[] content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(content);
return result;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length()/2];
for (int i = 0;i< hexStr.length()/2; i++) {
int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
}
成果截图
(实验五截图)
代码托管
实验总结
- 本次实验将之前的中缀后缀表达式和网络安全加密的知识结合起来,同时还学习客户端和服务器的相关知识,本次实验感觉收获很大,不过还有好多的内容感觉我还没有掌握的很好,还需要继续努力。
对结对伙伴的评价
- 我的结对伙伴陈是奇本次结对过程中十分积极,很多内容学习得更快更好,较上次的结对编程时进步很大,还需继续努力加油!