20172319 2018.06.13-20
实验五《网络编程与安全》 实验报告
课程名称:《程序设计与数据结构》
学生班级:1723班
学生姓名:唐才铭
学生学号:20172319
实验教师:王志强老师
课程助教:刘伟康、张旭升学长
实验时间:2018年6月13日——2018年6月20日
必修/选修:必修
目录
实验内容
- 参考数据结构应用
结对实现中缀表达式转后缀表达式的功能 MyBC.java
结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
上传测试代码运行结果截图和码云链接; - 结对编程:1人负责客户端,一人负责服务器
注意责任归宿,要会通过测试证明自己没有问题
基于Java Socket实现客户端/服务器功能,传输方式用TCP
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
客户端显示服务器发送过来的结果
上传测试代码运行结果截图和码云链接; - 加密结对编程:1人负责客户端,一人负责服务器
注意责任归宿,要会通过测试证明自己没有问题
基于Java Socket实现客户端/服务器功能,传输方式用TCP
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
客户端显示服务器发送过来的结果
上传测试结果截图和码云链接; - 密钥分发结对编程:1人负责客户端,一人负责服务器
注意责任归宿,要会通过测试证明自己没有问题
基于Java Socket实现客户端/服务器功能,传输方式用TCP
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
客户端和服务器用DH算法进行3DES或AES算法的密钥交换
服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
客户端显示服务器发送过来的结果
上传测试结果截图和码云链接; - 完整性校验结对编程:1人负责客户端,一人负责服务器
注意责任归宿,要会通过测试证明自己没有问题
基于Java Socket实现客户端/服务器功能,传输方式用TCP
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
客户端和服务器用DH算法进行3DES或AES算法的密钥交换
服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
客户端显示服务器发送过来的结果
上传测试结果截图和码云链接:
实验要求
- 完成蓝墨云上与实验五《网络编程与安全》相关的活动,及时提交代码运行截图和码云Git链接,截图要有学号水印,否则会扣分。
- 完成实验、撰写实验报告,实验报告以博客方式发表在博客园,注意实验报告重点是运行结果,遇到的问题(工具查找,安装,使用,程序的编辑,调试,运行等)、解决办法(空洞的方法如“查网络”、“问同学”、“看书”等一律得0分)以及分析(从中可以得到什么启示,有什么收获,教训等)。报告可以参考范飞龙老师的指导。
- 严禁抄袭,有该行为者实验成绩归零,并附加其他惩罚措施。
- 结对编程;也可自己在idea和虚拟机间进行传输;idea本身也可以进行服务器与客户端的传输。
实验步骤
- 完成蓝墨云上实验五 网络编程与安全-1
- 完成蓝墨云上实验五 网络编程与安全-2
- 完成蓝墨云上实验五 网络编程与安全-3
- 完成蓝墨云上实验五 网络编程与安全-4
- 完成蓝墨云上实验五 网络编程与安全-5
前期准备:
- 弄清自己的IP地址是什么:
Linux下查找IP的命令:ifconfig -a
Windows下查找IP的命令:ipconfig
Windows下查找IP(笨方法):开始——设置——网络和Internet——WLAN——硬件属性
需求分析:
- 需要在原Socket代码上加以理解并运用;
- 需要掌握、并会运用实验三java密码学所学的知识。
代码实现及解释
本次实验一共分为五个提交点:
-
任务1:
-
结对实现中缀表达式转后缀表达式的功能 MyBC.java;
结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java -
分别运用四则运算时的中缀转后缀、后缀求值的方法即可:
-
截图如下:
-
任务2:
-
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端。 -
弄懂Socket的两个代码,将传输信息的内容换掉即可。
-
对客户端代码的修改:
由
String info1 = "12 15 8 100 25 34 19";
变为
//客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
Scanner scanner = new Scanner(System.in);
System.out.println("请输入中缀表达式");
String info1 = scanner.nextLine();
info1 = MyBC.infixToSuffix(info1);
- 对服务器代码的修改:
由
String info=null;
if (!((info = bufferedReader.readLine()) ==null)){
System.out.println("我是服务器,用户信息为:" + info);
}
//给客户一个响应
//String reply=Output;
String reply = "welcome";
printWriter.write(reply);
变为
String info=null;
if (!((info = bufferedReader.readLine()) ==null)){
System.out.println("我是服务器,用户信息为:" + info);
}
//服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
String reply = MyDC.suffixToArithmetic(info);
printWriter.write(reply);
-
运行结果如下:
-
任务3:
-
让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端。 -
运用java密码学里的3DES算法;先对文件SEnc.java(加密)和SDec.java(解密)进行修改,使其从void变为String方法,返回密、明文;在客户端与服务器分别对需要传输的信息运用加解密方法。
-
客户端:
//客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式
Scanner scanner = new Scanner(System.in);
System.out.println("请输入中缀表达式");
String info1 = scanner.nextLine();
info1 = MyBC.infixToSuffix(info1);
//把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
try {
info1 = SEnc.encrypt(info1);
} catch (Exception e) {
e.printStackTrace();
}
String info = new String(info1.getBytes("GBK"),"utf-8");
// printWriter.write(info);
// printWriter.flush();
outputStreamWriter.write(info);
outputStreamWriter.flush();
socket.shutdownOutput();
- 服务器:
//4.读取用户输入信息
String info=null;
if (!((info = bufferedReader.readLine()) ==null)){
System.out.println("我是服务器,用户信息为:" + info);
}
//接收到后缀表达式表达式后,进行解密
try {
info = SDec.decode(info);
} catch (Exception e) {
e.printStackTrace();
}
//调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
String reply = MyDC.suffixToArithmetic(info);
printWriter.write(reply);
printWriter.flush();
-
运行结果:
-
任务4:
-
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
客户端和服务器用DH算法进行3DES或AES算法的密钥交换
服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端。 -
修改java密码学中的Key_DH.java的代码,运行后分别生成客户端与服务器的公私钥;
-
运行结果如图:
-
对客户端的代码修改:
//客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式
Scanner scanner = new Scanner(System.in);
System.out.println("请输入中缀表达式");
String info1 = scanner.nextLine();
info1 = MyBC.infixToSuffix(info1);
//把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
try {
info1 = SEnc.encrypt(info1);
} catch (Exception e) {
e.printStackTrace();
}
// 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
try{
// 读取对方的DH公钥
FileInputStream f1=new FileInputStream("Bpub.dat");
ObjectInputStream b1=new ObjectInputStream(f1);
PublicKey pbk=(PublicKey)b1.readObject( );
//读取自己的DH私钥
FileInputStream f2=new FileInputStream("Apri.dat");
ObjectInputStream b2=new ObjectInputStream(f2);
PrivateKey prk=(PrivateKey)b2.readObject( );
// 执行密钥协定
KeyAgreement ka=KeyAgreement.getInstance("DH");
ka.init(prk);
ka.doPhase(pbk,true);
System.out.println("
" + "公钥为:");
//生成共享信息
byte[ ] sb=ka.generateSecret();
for(int i=0;i<sb.length;i++){
System.out.print(sb[i]+",");
}
SecretKeySpec k=new SecretKeySpec(sb,"DESede");
} catch (Exception e) {
e.printStackTrace();
}
String info = new String(info1.getBytes("GBK"),"utf-8");
- 对服务器的代码修改:
String info=null;
if (!((info = bufferedReader.readLine()) ==null)){
System.out.println("我是服务器,用户信息为:" + info);
}
//接收到后缀表达式表达式后,进行解密
try {
info = SDec.decode(info);
} catch (Exception e) {
e.printStackTrace();
}
// 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
try{
// 读取对方的DH公钥
FileInputStream f1=new FileInputStream("Apub.dat");
ObjectInputStream b1=new ObjectInputStream(f1);
PublicKey pbk=(PublicKey)b1.readObject( );
//读取自己的DH私钥
FileInputStream f2=new FileInputStream("Bpri.dat");
ObjectInputStream b2=new ObjectInputStream(f2);
PrivateKey prk=(PrivateKey)b2.readObject( );
// 执行密钥协定
KeyAgreement ka=KeyAgreement.getInstance("DH");
ka.init(prk);
ka.doPhase(pbk,true);
System.out.println("
" + "公钥为:");
//生成共享信息
byte[ ] sb=ka.generateSecret();
for(int i=0;i<sb.length;i++){
System.out.print(sb[i]+",");
}
SecretKeySpec k=new SecretKeySpec(sb,"DESede");
} catch (Exception e) {
e.printStackTrace();
}
//调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
String reply = MyDC.suffixToArithmetic(info);
-
运行结果截图:
-
任务5:
-
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
客户端和服务器用DH算法进行3DES或AES算法的密钥交换
服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端 -
对客户端代码进行的修改:
//客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式
String a1,a2,a3,a4;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入中缀表达式");
String info1 = scanner.nextLine();
info1 = MyBC.infixToSuffix(info1); // 转后缀
a1 = info1 + "###";
String suffix = new String(a1.getBytes("GBK"),"utf-8");
outputStreamWriter.write(suffix);
outputStreamWriter.flush();
String cleartext = "",ciphertext = "";
//把后缀表达式用3DES或AES算法加密后通过网络把密文和明文的MD5値发送给服务器
try {
cleartext = DigestPass.MD5(info1); // 明文MID5
a2 = cleartext + "###";
String info_cleartext = new String(a2.getBytes("GBK"),"utf-8");
outputStreamWriter.write(info_cleartext);
outputStreamWriter.flush();
info1 = SEnc.encrypt(info1); // 加密
a3 = info1 + "###";
String encrypt = new String(a3.getBytes("GBK"),"utf-8");
outputStreamWriter.write(encrypt);
outputStreamWriter.flush();
ciphertext = DigestPass.MD5(info1); // 密文MID5
} catch (Exception e) {
e.printStackTrace();
}
// 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
try{
// 读取对方的DH公钥
FileInputStream f1=new FileInputStream("Bpub.dat");
ObjectInputStream b1=new ObjectInputStream(f1);
PublicKey pbk=(PublicKey)b1.readObject( );
//读取自己的DH私钥
FileInputStream f2=new FileInputStream("Apri.dat");
ObjectInputStream b2=new ObjectInputStream(f2);
PrivateKey prk=(PrivateKey)b2.readObject( );
// 执行密钥协定
KeyAgreement ka=KeyAgreement.getInstance("DH");
ka.init(prk);
ka.doPhase(pbk,true);
System.out.println("
" + "公钥为:");
//生成共享信息
byte[ ] sb=ka.generateSecret();
for(int i=0;i<sb.length;i++){
System.out.print(sb[i]+",");
}
SecretKeySpec k=new SecretKeySpec(sb,"DESede");
} catch (Exception e) {
e.printStackTrace();
}
String info_ciphertext = new String(ciphertext.getBytes("GBK"),"utf-8");
// printWriter.write(info);
// printWriter.flush();
outputStreamWriter.write(info_ciphertext);
- 对服务器代码进行的修改:
//4.读取用户输入信息
String info,info1,info2,info3,info4,info5 = null,reply = null;
info = bufferedReader.readLine();
System.out.println("我是服务器,用户信息为:" + info);
String[] history = info.split("###");
info1 = history[0];
System.out.println("我是服务器,用户明文为:" + info1);
info2 = history[1];
System.out.println("我是服务器,用户明文MID5值为:" + info2);
info3 = history[2];
System.out.println("我是服务器,用户密文为:" + info3);
info4 = history[3];
System.out.println("我是服务器,用户密文MID5值为:" + info4);
//接收到后缀表达式表达式后,进行解密并计算器MID5值
try {
info3 = SDec.decode(info3);
info5 = DigestPass.MD5(info3);
System.out.println("解密后求得的MID5:" + info5);
} catch (Exception e) {
e.printStackTrace();
}
// 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
try{
// 读取对方的DH公钥
FileInputStream f1=new FileInputStream("Apub.dat");
ObjectInputStream b1=new ObjectInputStream(f1);
PublicKey pbk=(PublicKey)b1.readObject( );
//读取自己的DH私钥
FileInputStream f2=new FileInputStream("Bpri.dat");
ObjectInputStream b2=new ObjectInputStream(f2);
PrivateKey prk=(PrivateKey)b2.readObject( );
// 执行密钥协定
KeyAgreement ka=KeyAgreement.getInstance("DH");
ka.init(prk);
ka.doPhase(pbk,true);
System.out.println("
" + "公钥为:");
//生成共享信息
byte[ ] sb=ka.generateSecret();
for(int i=0;i<sb.length;i++){
System.out.print(sb[i]+",");
}
SecretKeySpec k=new SecretKeySpec(sb,"DESede");
} catch (Exception e) {
e.printStackTrace();
}
//调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
if (info2.equals(info5)) {
reply = MyDC.suffixToArithmetic(info1);
}
else {
reply = "抱歉,二者的MID5值不一致!";
}
printWriter.write(reply);
- 运行结果截图:
测试过程及遇到的问题
-
问题1:'ipconfig' 不是内部或外部命令,也不是可运行的程序或批处理文件。
-
解决:起初为了试验能进行,采用了需求分析里的笨方法,后来发现是环境变量的问题,参考解决'不是内部或外部命令,也不是可运行的程序即可解决。
-
问题2:我作为客户端,伙伴作为服务器可以正常传输,而交换则显示
connect timed out
-
解决:当时电脑出现关于IDEA的警告,我允许了其行为,但并没有真正关掉防火墙,所以导致链接超时;
开始——设置——更新和安全——windows安全——防火墙和保护——关掉公共网络的防火墙即可
-
问题3:DH算法进行传输时两边的密钥不对等
-
解决:刚开始我对Key_DH里的方法做了修改:
- 分别运用到服务器和客户端中,结果:
很明显生成的公钥是不一样的,这明显不符合DH算法的原理;
下面针对客户端与服务器的代码进行分析:
可以明显看出执行到try之后,已经使用了Key_DH.java
中的client方法生成关于客户端的公私钥,这里显然没什么问题,
但要小心,此时服务器的公钥Bpub.dat
是读不到的,因为还没有任何操作进行相应的生成,之后当数据传输给服务器时,
其实客户端只读取了客户端自己的私钥,而传给服务器的,仅仅是用私钥进行运算后的;
再看服务器:
一样的道理,在向客户端传回数据前并没有读到客户端的公钥Apub.dat
,因此等同客户端,只进行了私钥的运算就传输了,
所以二者才不一样。 - 最后,将原文件Key_DH.java扩展成两个文件,直接运行生成二者的公私钥,在服务器与客户端不再进行生成操作,而是直接读取。
分析总结
- 本次实验,内容基本相关连,每一个都是在前者的基础上进行扩充,虽然看似简单,但其实也不太容易;回顾了java密码学的相关知识,学会了几种算法的基本应用,还学会如何在不同机器间进行传输。