2017-2018-2 20165214 实验五《网络编程与安全》实验报告

1、实验报告封面

课程:Java程序设计 班级:1652班 姓名:朱文远 学号:20165214html

指导教师:娄嘉鹏 实验日期:2018年5月28日java

实验时间:15:35 - 17:15 实验序号:五git

实验名称: 网络编程与安全算法

实验目的:
一、掌握Java Socket的相关内容;
二、学会创建客户端与服务器端之间的联系;
三、学习并应用密码学的相关内容编程

2、实验内容

任务(一)

  • 一、结对实现中缀表达式转后缀表达式的功能 MyBC.java
  • 二、结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java

任务(二)

  • 一、基于Java Socket实现客户端/服务器功能,传输方式用TCP
  • 二、客户端让用户输入中缀表达式,而后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式经过网络发送给服务器
  • 三、服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  • 四、客户端显示服务器发送过来的结果

任务(三)

  • 一、基于Java Socket实现客户端/服务器功能,传输方式用TCP
  • 二、客户端让用户输入中缀表达式,而后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后经过网络把密文发送给服务器
  • 三、服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,能够用数组保存),而后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  • 四、客户端显示服务器发送过来的结果

任务(四)

  • 一、基于Java Socket实现客户端/服务器功能,传输方式用TCP
  • 二、客户端让用户输入中缀表达式,而后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密经过网络把密文发送给服务器
  • 三、客户端和服务器用DH算法进行3DES或AES算法的密钥交换
  • 四、服务器接收到后缀表达式表达式后,进行解密,而后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  • 五、客户端显示服务器发送过来的结果

任务(五)

  • 一、基于Java Socket实现客户端/服务器功能,传输方式用TCP数组

  • 二、客户端让用户输入中缀表达式,而后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密经过网络把密文和明文的MD5値发送给服务器安全

  • 三、客户端和服务器用DH算法进行3DES或AES算法的密钥交换服务器

  • 四、服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端网络

  • 五、客户端显示服务器发送过来的结果多线程

  • 最后将每一个任务的代码上传到码云。

3、实验步骤

任务(一)的实现

  • 任务(一)早在结队编程—四则运算中咱们就已经实现了中缀转后缀的功能,以及将后缀表达式利用栈进行运算的功能。所以任务(一)咱们直接在以前代码的基础上进行修改便可。

  • 代码运行结果以下,能够多种符号的四则运算。

任务(二)的实现

  • 经过java.net包中的Socket类和ServerSocket类来实现这个功能。这时候我简单回顾了以前我写过的博客,从中复习一下这两个类的具体功能:

套接字:IP地址表示计算机,端口号表示进程(线程),Socket类建立套接字对象并链接在一块儿(端口号与IP地址组合)。
客户端程序用Socket类建立负责链接到服务器的套接字对象,其构造方法为Socket([IP地址],[端口号])(可能抛出IOException异常)。对套接字对象创建后,可使用①getInputStream()得到一个输入流来读取服务器写入到输出流中的数据;②getOutputStream()得到一个输出流,服务器能够用输入流来读取客户写入到输出流中的数据。
客户负责创建链接到服务器的套接字对象。服务器须要建立一个ServerSocket对象来将客户端的套接字对象与服务器的套接字对象链接起来。ServerSocket的构造方法是ServerSocket([端口号])(当端口已被占用会抛出IOException异常)。接着,ServerSocket对象调用accept()方法再次返回一个与客户端对象相链接的新的Socket对象。一样的,它也具备上述的两个方法。
从套接字链接中读取数据,可能在另外一端数据发送以前就已经开始读取了,并且会阻塞本线程,直到成功读取到信息。同时,accept方法也会阻塞线程的执行,直到收到客户的呼叫。为了解决“收不到呼叫而致使程序没法继续运行”的状况,ServerSocket对象在调用accept方法以前能够先调用setTimeout(s)方法来使得在调用accept方法时若是超过s毫秒没有收到呼叫,就抛出SocketTimeoutException异常。
双方通讯完毕,套接字要使用close()方法关闭套接字链接。
使用多线程技术:因为使用套接字链接中读取数据时,可能会阻塞本线程直到成功读取到信息。为了不这种状况,须要启动一个专门为该客户服务的线程。Socket的构造方法Socket()能够建立一个套接字对象,该对象调用public void connect(SocketAddress endpoint) throws IOException来与指定的套接字建立链接。这里的参数可使用InetSocketAddress的构造方法public InetSocketAdress(InetAdress addr,int port)来得到。套接字通讯的两个基本原则:
①服务器要启动一个专门的线程与客户的套接字创建链接;
②套接字的输入流在读取信息时可能发生阻塞,因此客户端与服务器端都须要在一个单独的线程中读取信息。
-- 引用自个人第九周学习任务

  • 根据这段提示还有教材的帮助,完成了代码部分:
  • 客户端关键代码
System.out.print("输入中缀表达式:");
        while(scanner.hasNext()) {
            String middle="";//初始化middle
            try {
                middle = scanner.next();//输入中缀表达式
            }
            catch(InputMismatchException exp){
                System.exit(0);
            }
            try {
                myBC.setNormal(middle);
                myBC.change();//调用M有BC中的类来将中缀表达式转化为后缀表达式。
                System.out.println("后缀表达式:"+myBC.Behind); //Behind是类变量,因此能够经过类名来调用。
                out.writeUTF(myBC.Behind);//将后缀表达式写进流中

            }
            catch(Exception e) {}
        }
  • Read类输出结果
...
public void run() {
        String result="";
        while(true) {
            try{ result=in.readUTF();//读取从服务器端传输回来的结果
                System.out.println("结果:"+result);
                System.out.println("输入中缀表达式:");//能够再次输入新的表达式进行运算。
            }
            catch(IOException e) {
                System.out.println("与服务器已断开"+e);
                break;
            }
        }
    }
 ...
  • 服务器端关键代码
...
@Override
    public void run() {
        while(true) {
            try{  String r=in.readUTF();//堵塞状态,除非读取到信息
                String result=caculate.caculate(r);//调用Caculate类进行运算
                out.writeUTF(result);//将结果写入到流中
            }
            catch (IOException e) {
                System.out.println("客户离开");
                return;
            }
        }
    }
 ...
  • 运行代码,结果如图(第一个图是客户端截图,第二个图是服务器端截图):
    客户端截图

服务器端截图

任务(三)的实现

  • 参考了以前实验三时娄老师的博客:Java 密码学算法,使用了DES算法。
  • 加密部分代码
...
String cipher=sEnc.Cipher(myBC.Behind);//将后缀表达式加密
out.writeUTF(cipher);//将加密结果写入到流中让服务器端读取
 ...
  • 解密部分代码
...
String r=in.readUTF();//读取客户端传过来的密文
String plain=sDec.Plain(r);//解密
String result=caculate.caculate(plain);//将解密获得的后缀表达式进行计算,而后返回计算结果。
 ...
  • 运行代码,结果如图

任务(四)的实现

  • 目前还没接触到DH算法这个概念,因而先上网查找相关内容。

Diffie-Hellman是一种创建密钥的方法,而不是加密方法。然而,它所产生的密钥可用于加密、进一步的密钥管理或任何其它的加密方式。
基于原根的定义及性质,能够定义Diffie-Hellman密钥交换算法.该算法描述以下:
1,有两个全局公开的参数,一个素数q和一个整数a,a是q的一个原根.
2,假设用户A和B但愿交换一个密钥,用户A选择一个做为私有密钥的随机数XA(XA<q),并计算公开密钥YA=a^XA mod q。A对XA的值保密存放而使YA能被B公开得到。相似地,用户B选择一个私有的随机数XB<q,并计算公开密钥YB=a^XB mod q。B对XB的值保密存放而使YB能被A公开得到.
3,用户A产生共享秘密密钥的计算方式是K = (YB)^XA mod q.一样,用户B产生共享秘密密钥的计算是K = (YA)^XB mod q.这两个计算产生相同的结果: K = (YB)^XA mod q = (a^XB mod q)^XA mod q = (aXB)XA mod q (根据取模运算规则获得) = a^(XBXA) mod q = (aXA)XB mod q = (a^XA mod q)^XB mod q = (YA)^XB mod q 所以至关于双方已经交换了一个相同的秘密密钥.
4,由于XA和XB是保密的,一个敌对方能够利用的参数只有q,a,YA和YB.于是敌对方被迫取离散对数来肯定密钥.例如,要获取用户B的秘密密钥,敌对方必须先计算 XB = inda,q(YB) 而后再使用用户B采用的一样方法计算其秘密密钥K. Diffie-Hellman密钥交换算法的安全性依赖于这样一个事实:虽然计算以一个素数为模的指数相对容易,但计算离散对数却很困难.对于大的素数,计算出离散对数几乎是不可能的. 下面给出例子.密钥交换基于素数q = 97和97的一个原根a = 5.A和B分别选择私有密钥XA = 36和XB = 58.每人计算其公开密钥 YA = 5^36 = 50 mod 97 YB = 5^58 = 44 mod 97 在他们相互获取了公开密钥以后,各自经过计算获得双方共享的秘密密钥以下: K = (YB)^XA mod 97 = 44^36 = 75 mod 97 K = (YA)^XB mod 97 = 50^58 = 75 mod 97 从|50,44|出发,攻击者要计算出75很不容易.
-- 引用自百度百科

任务(五)的实现

  • 在任务(四)的基础上引入MD5算法加密的类,调用其余的函数,参数为要加密的字符串,返回加密后的字符串。
  • 参考了娄老师的博客:Java 密码学算法中的MD5算法讲解。
  • 加密代码
...
public String MD5(String cipher) throws Exception{
        MessageDigest m=MessageDigest.getInstance("MD5");
        m.update(cipher.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);//将加密结果转化为16进制字符串
        }
        System.out.println("MD5加密后的结果:"+result);//输出加密后的结果
        return  result;//返回加密后的结果。
 ...
  • 服务器端中改动的代码:
...
String r=in.readUTF();//读取信息到r中(r是密文)
String plain=sDec.Plain(r);//将r解密获得明文
String message=digestPass.MD5(plain);//将解密后获得的明文用MD5算法加密,放入message中
String result=caculate.caculate(plain);//将解密后获得的算式进行运算
String q=in.readUTF();//获得用户端传送过来用MD5加密的明文q。
if(message.equals(q)){ //比较message和q,若是相等则校验成功,将计算结果返回。
System.out.println("校验成功!");
out.writeUTF(result);
   ...
  • 客户端中改动的代码:
...
String cipher=sEnc.Cipher(myBC.Behind);//将后缀表达式使用DES算法加密,获得密文cipher
out.writeUTF(cipher);把密文cipher写入流中以让服务器端读取。
out.writeUTF(digestPass.MD5(myBC.Behind));//将后缀表达式(明文)使用MD5算法加密,而后写入流中以让服务器端读取。
   ...
  • 运行结果如图

4、码云连接

5、遇到的问题

  • 问题1:在调试代码的时候,有时候运行服务器端的程序会出现这样的问题:

  • 问题1解决:缘由是我没有将以前的服务器端口关闭,而是开了多个服务器端口,却使用了一样的端口,因此致使了这个状况。把以前的端口先关掉,问题就解决了。

6、总结

本次实验让我对网络编程与安全有了初步的了解,同时对于Java的密码学应用相比以前有了更深入的认识。此次实验让我在Java、计算机网络、密码学三个方面有所学习有所进步,能够说是一举三得。所以我以为这是一次颇有意义的实验。

相关文章
相关标签/搜索