20172321 2017-2018-2 《程序设计与数据结构》实验五报告
20172321 2017-2018-2 《程序设计与数据结构》实验五报告
课程:《程序设计与数据结构》
班级: 1723
姓名: 吴恒佚
学号:20172321
实验教师:王志强
实验日期:2018年6月13日~2018年6月18日
必修/选修: 必修
一、实验内容
1、网络编程与安全-1
- 两人一组结对编程:
- a. 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA
- b. 结对实现中缀表达式转后缀表达式的功能 MyBC.java
- c. 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
- d. 上传测试代码运行结果截图和码云链接
2、网络编程与安全-2
- 结对编程:1人负责客户端,一人负责服务器
- a. 注意责任归宿,要会通过测试证明自己没有问题
- b. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- c. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
- d. 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- e. 客户端显示服务器发送过来的结果
- f. 上传测试结果截图和码云链接
3、网络编程与安全-3
- 加密结对编程:1人负责客户端,一人负责服务器
- a. 注意责任归宿,要会通过测试证明自己没有问题
- b. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- c. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
- d. 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- e. 客户端显示服务器发送过来的结果
- f. 上传测试结果截图和码云链接
4、网络编程与安全-4
- 密钥分发结对编程:1人负责客户端,一人负责服务器
- a. 注意责任归宿,要会通过测试证明自己没有问题
- b. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- c. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
- d. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- e. 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- f. 客户端显示服务器发送过来的结果
- g. 上传测试结果截图和码云链接
5、网络编程与安全-5
- 完整性校验结对编程:1人负责客户端,一人负责服务器
- a. 注意责任归宿,要会通过测试证明自己没有问题
- b. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- c. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
- d. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- e. 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- f. 客户端显示服务器发送过来的结果
- g. 上传测试结果截图和码云链接
二、实验过程及结果
1、网络编程与安全-1
- 首先,第一个节点是让我们实现之前已经实现的四则运算里面的转后缀式以及计算一个后缀式的值,虽然在娄老师的博客里面给了两个方法进行对于一个中缀表达式转化成一个后缀表达式,但是据说这两个代码不是完全体,所以我们还是决定去使用了自己之前已经写好的代码进行对于实验目标的实现。
- 因为这个步骤基本上只是重复一遍四则运算的步骤,所以不多做叙述。
- 结果
2、网络编程与安全-2
- 因为是第一次学习并且使用Java Socket实现客户端/服务器功能,传输方式用TCP进行实验,因此一开始老师给我们两个客户端和服务器的示意代码,所以这一个实验与其说是进行代码编写不如说是进行代码的补充和修改。首先这一个实验任务就是先构建一个服务器和客户端的连接,然后客户端将一个已经转为后缀式的中缀表达式传给服务器,然后服务器接收到这个中缀表达式,进行计算,通过网络编程与安全-1的实验代码进行计算的实现。
- 因为服务器和客户端的示范代码已经给我们了,这一步也只是稍做修改,再补充上中缀表达式的转换和计算功能,再让后缀表达式和答案可以在服务器和客户端上交流就可以了。
- 客户端
- 首先,把这个地方改成自己要链接的服务器地址
//1.建立客户端Socket连接,指定服务器位置和端口 //Socket socket = new Socket("localhost",8080); Socket socket = new Socket("192.168.1.113", 8800);
- 然后加上输入中缀表达式的代码和之前已有的转换为后缀的方法
String infix; System.out.println("输入你想计算的中缀表达式(不需要输入空格,enter键结束输入):"); Scanner scan = new Scanner(System.in); infix = scan.nextLine(); String postfix, buzhou; MyBC theTrans = new MyBC(infix); postfix = theTrans.doTrans();
- 最后传输和接收的代码是示范代码自带的
- 服务器
- 接收和回传的代码也是已有的
- 把后缀表达式计算出来就可以了
String expressions; MyDC evaluator = new MyDC(); expressions = info; result = evaluator.evaluate(expressions);
- 结果
3、网络编程与安全-3
- 在这一个实验中,我们必须需要用到之前已经学习过的Java与密码学的练习那一个实验的相关知识,
我们不得不去温习一下加密算法有哪些类型,在之前我们粗略的了解过的有这样几种:3DES、AES、RSA,MD5以及一个DH密钥交换协议的学习,在接下来的实验中,我们要依次运用到以上的相关知识。 - 这次试验的目的是客户端需要输入一个中缀表达式,将它转换成一个后缀表达式并且进行加密,然后传递给服务器,服务器将其进行解密,然后将解出来的后缀式进行计算并回传。
- 要用到之前实验的两个类
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); } }
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]+","); } } }
- 客户端进行加密
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[] = postfix.getBytes("UTF8"); System.out.print("加密后缀表达式:"); for (int i = 0; i < ptext.length; i++) { System.out.print(ptext[i] + ","); } System.out.println(""); byte ctext[] = cp.doFinal(ptext);//进行加密。 String aa = ""; for (int i = 0; i < ctext.length; i++) { aa += ctext[i] + ","; } String info3 = aa; String info = new String(info3.getBytes("GBK"), "utf-8"); ```
- 服务器解密
FileInputStream f3 = new FileInputStream("keykb1.dat"); int num2 = f3.available(); byte[] keykb = new byte[num2]; f3.read(keykb); SecretKeySpec k = new SecretKeySpec(keykb, "DESede"); // 解密 Cipher cp = Cipher.getInstance("DESede"); cp.init(Cipher.DECRYPT_MODE, k); byte[] ptext = cp.doFinal(ab); // 显示明文 String p = new String(ptext, "UTF8"); System.out.println("解密的后缀表达式:" + p);
- 结果
4、网络编程与安全-4
-
这一部分的实验要求我们需要生成新的密钥从而进行加密和解密的过程,而这个产生新密钥的过程就是需要用到我们之前学到的DH交换密钥协议,这里先说一下我对于DH算法的理解:
-
由于DH算法需要A和B二人各自生成DH公钥和私钥,因此在这两个人都在自己的目录下都拷贝编译后文件Key_DH。
-
首先由A创建自己的公钥和私钥,即A进行“java Key_DH Apub.dat Apri.dat”运行程序,这时在目录下将产生文件Apub.dat和Apri.dat,前者保存着A的公钥,后者保存着A的私钥。
-
然后由B创建自己的公钥和私钥,即B进行“java Key_DH Bpub.dat Bpri.dat”运行程序,这时在目录B下将产生文件Bpub.dat和Bpri.dat,前者保存着B的公钥,后者保存着B的私钥。
-
最后发布公钥,将Apub.dat拷贝到B的目录,将Bpub.dat拷贝到A的目录。
这样,A、B双方的DH公钥和私钥已经创建并部署完毕。
-
-
需要用到这两个类
import javax.crypto.KeyAgreement; import javax.crypto.spec.SecretKeySpec; import java.io.FileInputStream; import java.io.ObjectInputStream; import java.security.PrivateKey; import java.security.PublicKey; public class KeyAgree { public static void main(String args[]) throws Exception { // 读取对方的DH公钥 FileInputStream f1 = new FileInputStream(args[0]); ObjectInputStream b1 = new ObjectInputStream(f1); PublicKey pbk = (PublicKey) b1.readObject(); //读取自己的DH私钥 FileInputStream f2 = new FileInputStream(args[1]); 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(); for (int i = 0; i < sb.length; i++) { System.out.print(sb[i] + ","); } SecretKeySpec k = new SecretKeySpec(sb, "DESede"); } }
import javax.crypto.spec.DHParameterSpec; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; public class Key_DH { //三个静态变量的定义从 // C:\j2sdk-1_4_0-doc\docs\guide\security\jce\JCERefGuide.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 main(String args[]) 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(args[0]); ObjectOutputStream b1 = new ObjectOutputStream(f1); b1.writeObject(pbk); // 保存私钥 FileOutputStream f2 = new FileOutputStream(args[1]); ObjectOutputStream b2 = new ObjectOutputStream(f2); b2.writeObject(prk); } }
-
结果
5、网络编程与安全-5
- 第五个实验是关于MD5关于Java的摘要算法
- 计算明文的MD5值
public String Diget(String x) throws UnsupportedEncodingException, NoSuchAlgorithmException { 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; }
- 进行对比,如果一样,则计算,不一样则抛出异常
DigestPass ooo=new DigestPass(); String reply; if (ww.equals(ooo.Diget(p))) { reply= String.valueOf(a.suanshu(ls)); } else { reply="返回的MD5值不同"; }
- 结果
三、实验过程中遇到的问题和解决过程
- 在进行实验5-3的时候,我先在自己的电脑上进行了实验,加完密之后总显示找不到文件,后来我试着调了一下,然后就找不到主类了,但是我没有放弃,继续坚持调试,终于所有程序运行的结果都是乱码了。
- 于是我决定寻找帮助,学长先试着看了一下我的设置,觉得没什么问题,最后他开始百度了,发现应该这样:打开IntelliJ IDEA 14.0安装路径,小编的安装路径为:D:\Program Files\JetBrains\IntelliJ IDEA 14.0\bin 找到idea.exe.vmoptions 文件,用记事本打开,在最后一行填加:“-Dfile.encoding=UTF-8”,如图所示,保存。终于解决了问题,嘿嘿嘿。
- 在进行实验5-4的时候,我和我亲爱的队友在各自的电脑上同时研究服务器和客户端,我们自己向自己进行传输回传都很顺利,但是我们俩相互传输就会出错,后来发现我们当时没有注意到我们自己进行是是建立两个目录A和B,模拟需要秘密通信的A、B双方,我们两个没有用对方的公钥生成密匙,所以不能解密。
- 我们在创建共享密钥的时候思路比较混乱,还出来错误,然后正好学长注意到了我们,帮我们调试了一番,不过因此我们互换了服务器和客户端的位置,还出了一个小插曲,后来发现是因为我自己修改了后缀表达式的类,但是她没改,就有一些不匹配了。
四、其他(感悟、思考等)
光阴似箭、岁月如梭,时间如白马过隙一去不返,细碎的时光从我们敲打键盘的之间流过。。。简而言之,这一学期就要结束了,世界杯也开始了,但是,我们绝对不能放弃学习Java,一定要坚持,差不多就酱。