zoukankan      html  css  js  c++  java
  • 2017-2018-2 20165214 实验五《网络编程与安全》实验报告

    一、实验报告封面

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

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

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

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

    实验目的:
    1、掌握Java Socket的相关内容;
    2、学会建立客户端与服务器端之间的联系;
    3、学习并应用密码学的相关内容

    二、实验内容

    任务(一)

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

    任务(二)

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

    任务(三)

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

    任务(四)

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

    任务(五)

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

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

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

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

    • 5、客户端显示服务器发送过来的结果

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

    三、实验步骤

    任务(一)的实现

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

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

    任务(二)的实现

    • 通过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算法加密,然后写入流中以让服务器端读取。
       ...
    
    • 运行结果如图

    四、码云链接

    五、遇到的问题

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

    • 问题1解决:原因是我没有将之前的服务器端口关闭,而是开了多个服务器端口,却使用了同样的端口,所以导致了这个情况。把之前的端口先关掉,问题就解决了。

    六、总结

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

  • 相关阅读:
    洛谷 P1508 Likecloud-吃、吃、吃
    Codevs 1158 尼克的任务
    2017.10.6 国庆清北 D6T2 同余方程组
    2017.10.6 国庆清北 D6T1 排序
    2017.10.3 国庆清北 D3T3 解迷游戏
    2017.10.3 国庆清北 D3T2 公交车
    2017.10.3 国庆清北 D3T1 括号序列
    2017.10.4 国庆清北 D4T1 财富
    2017.10.7 国庆清北 D7T2 第k大区间
    2017.10.7 国庆清北 D7T1 计数
  • 原文地址:https://www.cnblogs.com/zhuwenyuan/p/9095523.html
Copyright © 2011-2022 走看看