20172302《程序设计与数据结构》实验五 网络编程与安全实验报告

课程:《程序设计与数据结构》

班级: 1723

姓名: 侯泽洋

学号:20172302

实验教师:王志强老师

实验日期:2018年6月13日

必修/选修: 必修

1.实验内容

  • (1)结对实现中缀表达式转后缀表达式的功能 MyBC.java
    结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
    上传测试代码运行结果截图和码云链接

  • (2)结对编程:1人负责客户端,一人负责服务器
    注意责任归宿,要会通过测试证明自己没有问题
    基于Java Socket实现客户端/服务器功能,传输方式用TCP
    客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
    服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
    客户端显示服务器发送过来的结果
    上传测试结果截图和码云链接

  • (3)加密结对编程:1人负责客户端,一人负责服务器
    注意责任归宿,要会通过测试证明自己没有问题
    基于Java Socket实现客户端/服务器功能,传输方式用TCP
    客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
    服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
    客户端显示服务器发送过来的结果
    上传测试结果截图和码云链接

  • (4)密钥分发结对编程:1人负责客户端,一人负责服务器
    注意责任归宿,要会通过测试证明自己没有问题
    基于Java Socket实现客户端/服务器功能,传输方式用TCP
    客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
    客户端和服务器用DH算法进行3DES或AES算法的密钥交换
    服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
    客户端显示服务器发送过来的结果
    上传测试结果截图和码云链接

  • (5)完整性校验结对编程:1人负责客户端,一人负责服务器
    注意责任归宿,要会通过测试证明自己没有问题
    基于Java Socket实现客户端/服务器功能,传输方式用TCP
    客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
    客户端和服务器用DH算法进行3DES或AES算法的密钥交换
    服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
    客户端显示服务器发送过来的结果
    上传测试结果截图和码云链接

2.实验过程及结果

(1)实验一

这里是要求我们:①结对实现中缀表达式转后缀表达式的功能 MyBC.java,②结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能。
因为我们前面已经做完了结对编程任务——四则运算,这里做起来就比较简单,将之前的两个类调用过来,编写一个驱动类即可完成实验一,这里是驱动类的代码:

public class MyDC
{
    public static void main(String[] args) {
        Questions questions = new Questions();
        String  question = String.valueOf(questions.getQuestion());
        System.out.println("问题:"+question);
        suffix suffix = new suffix();
        String answer = suffix.getAnswer(question);
        System.out.println("后缀:"+answer);
        Calculations calculation = new Calculations();
        String result = calculation.getresult(answer);
        System.out.println("求值:"+result);
    }
}

具体的每个类的代码这里不再展示,可参见码云链接:实验一
运行的结果截图如图:

(2)实验二

要求是:①结对编程:一人负责客户端,另一人负责服务器;②注意责任归宿,要会通过测试证明自己没有问题;③基于Java Socket实现客户端/服务器功能,传输方式用TCP;④客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器;⑤服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端;⑥客户端显示服务器发送过来的结果。
这里是首先需要了解Socket通信是什么:

网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。

再来了解以下Socket通信的过程是怎样的:
Server 端Listen(监听)某个端口是否有连接请求,Client端向Server端发出Connect(连接)请求,Server端向Client端发回 Accept(接受)消息。一个连接就建立起来了。Server端和Client端都可以通过Send,Write等方法与对方通信。
对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:
-(1) 创建Socket;
-(2) 打开连接到Socket的输入/出流;
-(3) 按照一定的协议对Socket进行读/写操作;
-(4) 关闭Socket.(在实际应用中,并未使用到显示的close,虽然很多文章都推荐如此,不过在我的程序中,可能因为程序本身比较简单,要求不高,所以并未造成什么影响。)

简单了解以后,我们根据老师所给的初始代码进行修改,这里是我充当的角色为服务器,即需接收客户端传来的后缀表达式,并进行计算求值,再将结果返回给客户端,这里的代码见下:

package exp5;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;
/**
 * Created by besti on 2018/6/9.
 */
public class SocketServer {
    public static void main(String[] args) throws IOException {
        //1.建立一个服务器Socket(ServerSocket)绑定指定端口
        ServerSocket serverSocket=new ServerSocket(8800);
        //2.使用accept()方法阻止等待监听,获得新连接
        Socket socket=serverSocket.accept();
        //3.获得输入流
        InputStream inputStream=socket.getInputStream();
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        //获得输出流
        OutputStream outputStream=socket.getOutputStream();
        PrintWriter printWriter=new PrintWriter(outputStream);
        //4.读取用户输入信息
        String info=null;
        info = bufferedReader.readLine();
        System.out.println("我是20172302侯泽洋,用户20172308周亚杰发送信息为:" + info);
        Calculations calculations = new Calculations();
        String reply = calculations.getresult(info);
        //给客户一个响应
        printWriter.write(reply);
        printWriter.flush();
        //5.关闭资源
        printWriter.close();
        outputStream.close();
        bufferedReader.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}

客户端的代码是归伙伴负责,这里将码云链接给出:
实验二

运行结果截图:

(3)实验三

题目:①客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器;②服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端。

这里是要求的我们在实验二的基础之上,把传输内容进行加密,不再进行明文传输,又需要用到我们所做的实验三的密码学算法的内容,又把之前的内容复习了一遍,关于DES加密的过程这里不再进行阐述,可参见实验三博客内容。
这里需要传输的密文是一个byte型的数组,我们采用字节流直接对它进行传输和接收,我负责的服务器端具体代码见下:

package exp5;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.StringTokenizer;
import static java.awt.SystemColor.info;
/**
 * Created by besti on 2018/6/9.
 */
public class SocketServer {
    public static void main(String[] args) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        //1.建立一个服务器Socket(ServerSocket)绑定指定端口
        ServerSocket serverSocket=new ServerSocket(8800);
        //2.使用accept()方法阻止等待监听,获得新连接
        Socket socket=serverSocket.accept();
        //3.获得输入流
        InputStream inputStream=socket.getInputStream();
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        //获得输出流
        OutputStream outputStream=socket.getOutputStream();
        PrintWriter printWriter=new PrintWriter(outputStream);
        //4.读取用户输入信息
        byte[] ctext = inputStream.readAllBytes();
        for(int i=0;i<ctext.length;i++){
            System.out.print(ctext[i]+",");
        }
        System.out.println();
        // 获取密钥
        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(ctext);
        // 显示明文
        String p=new String(ptext,"UTF8");
        System.out.println(p);
        Calculations calculations = new Calculations();
        String reply = calculations.getresult(p);
        //给客户一个响应
        printWriter.write(reply);
        printWriter.flush();
        //5.关闭资源
        printWriter.close();
        outputStream.close();
        bufferedReader.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}

伙伴负责的是客户端的部分,码云链接为:实验三
运行结果截图:

(4)实验四

实验题目:在实验三基础上进行客户端和服务器用DH算法进行3DES或AES算法的密钥交换,这里我们选择的是DH算法进行AES算法的密钥交换,
创建DH算法的公钥和私钥:

package experiment4;
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);
    }
}

接下来我们交换了各自产生的公钥文件,然后将由公钥私钥配对产生密钥的过程放入到我们的服务器和客户端的运行过程中,服务器端完成的代码见下:

package exp5;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.StringTokenizer;
import static java.awt.SystemColor.info;
/**
 * Created by besti on 2018/6/9.
 */
public class SocketServer {
    public static void main(String[] args) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, ClassNotFoundException {
        //1.建立一个服务器Socket(ServerSocket)绑定指定端口
        ServerSocket serverSocket=new ServerSocket(9901);
        //2.使用accept()方法阻止等待监听,获得新连接
        Socket socket=serverSocket.accept();
        //3.获得输入流
        InputStream inputStream=socket.getInputStream();
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        //获得输出流
        OutputStream outputStream=socket.getOutputStream();
        PrintWriter printWriter=new PrintWriter(outputStream);
        //4.读取用户输入信息
        byte[] ctext = inputStream.readAllBytes();
        for(int i=0;i<ctext.length;i++){
            System.out.print(ctext[i]+",");
        }
        System.out.println();
        // 读取对方的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);
        //生成共享信息
        byte[ ] sb=ka.generateSecret();
        for(int i=0;i<sb.length;i++){
            System.out.print(sb[i]+",");
        }
        SecretKeySpec k=new  SecretKeySpec(sb,0,24,"AES");
        // 解密
        Cipher cp=Cipher.getInstance("AES");
        cp.init(Cipher.DECRYPT_MODE, k);
        byte[] ptext  = cp.doFinal(ctext);
        // 显示明文
        String p=new String(ptext,"UTF8");
        System.out.println(p);
        Calculations calculations = new Calculations();
        String reply = calculations.getresult(p);
        //给客户一个响应
        printWriter.write(reply);
        printWriter.flush();
        //5.关闭资源
        printWriter.close();
        outputStream.close();
        bufferedReader.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}

客户端由伙伴完成,码云链接:实验四
运行结果截图:



(5)实验五

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

仍然需要复习MD5值的加密过程,以下为MD5加密的代码过程:

            String x = info;
            MessageDigest m2 = MessageDigest.getInstance("MD5");
            m2.update(x.getBytes());
            byte a[] = m2.digest();
            String result = "";
            for (int i = 0; i < a.length; i++) {
                result += Integer.toHexString((0x000000ff & a[i]) | 0xffffff00).substring(6);
            }
            System.out.println(result);
            out.println(result);//通过网络将明文的Hash函数值传送到服务器
            str = in.readLine();// 从网络输入流读取结果
            System.out.println("从服务器接收到的结果为:" + str); // 输出服务器返回的结果

然后我们将MD5值以String类型数据进行传输,我进行接收,我负责的服务器端的代码:

package exp5;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.*;
import java.util.StringTokenizer;
import static java.awt.Color.white;
import static java.awt.SystemColor.info;
/**
 * Created by besti on 2018/6/9.
 */
public class SocketServer {
    public static void main(String[] args) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, ClassNotFoundException {
        //1.建立一个服务器Socket(ServerSocket)绑定指定端口
        ServerSocket serverSocket=new ServerSocket(9901);
        //2.使用accept()方法阻止等待监听,获得新连接
        Socket socket=serverSocket.accept();
        //3.获得输入流
        InputStream inputStream=socket.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"UTF-8");
        BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
        //获得输出流
        OutputStream outputStream=socket.getOutputStream();
        PrintWriter printWriter=new PrintWriter(outputStream);
        //4.读取用户输入信息
        String[] array = new String[2];
        String info= null;
        String in =null;
        int i = 0;
        while(!((info = bufferedReader.readLine()) ==null)) {
            array[i] = info;
            i++;
        }
        String ra = array[0];
        StringTokenizer stringTokenizer = new StringTokenizer(ra);
        System.out.println("客户端传来的密文:"+ra);
        byte[]  ctext = {-64,-111,-17,100,71,-119,-4,57,-25,-110,-24,-58,-68,48,-124,-126};
        System.out.println();
        String ai = array[1];
        System.out.println("客户端传来的MD5值:"+ai);
        System.out.println();
        // 读取对方的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);
        //生成共享信息
        byte[ ] sb=ka.generateSecret();
        System.out.println("公钥、私钥配对产生密钥为:");
        for(int x=0;x<sb.length;x++){
            System.out.print(sb[x]+",");
        }
        SecretKeySpec k=new  SecretKeySpec(sb,0,24,"AES");
        // 解密
        Cipher cp=Cipher.getInstance("AES");
        cp.init(Cipher.DECRYPT_MODE, k);
        byte[] ptext  = cp.doFinal(ctext);
        // 显示明文
        String p=new String(ptext,"UTF8");
        System.out.println();
        System.out.println(p);
        MessageDigest m=MessageDigest.getInstance("MD5");
        m.update(p.getBytes("UTF8"));
        byte s[ ]=m.digest( );
        String result="";
        for (int u=0; u<s.length; u++){
            result+=Integer.toHexString((0x000000ff & s[u]) |
                            0xffffff00).substring(6);
                }
                System.out.println("解密后得到的后缀表达式的MD5值"+result);
        if (result.equals(ai)){
            System.out.println("明文的MD5和客户端传来的MD5值相等,则计算并求值发送给客户端");
            Calculations calculations = new Calculations();
            String reply = calculations.getresult(p);
            //给客户一个响应
            printWriter.write(reply);
            printWriter.flush();
        }
        //5.关闭资源
        printWriter.close();
        outputStream.close();
        bufferedReader.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}

伙伴的代码见下:

package experiment4;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.Socket;
import java.security.*;
import java.util.Scanner;
/**
 * Created by besti on 2018/6/9.
 */
public class SocketClient {
    public static void main(String[] args) throws IOException, InvalidKeyException, ClassNotFoundException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException {
        //1.建立客户端Socket连接,指定服务器位置和端口
//        Socket socket = new Socket("localhost",8080);
        Socket socket = new Socket("172.16.43.121",9901);
        //2.得到socket读写流
        OutputStream outputStream = socket.getOutputStream();
        //       PrintWriter printWriter = new PrintWriter(outputStream);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        //输入流
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        //3.利用流按照一定的操作,对socket进行读写操作
        System.out.println("请输入一个问题:");//输入中缀
        Scanner scan =new Scanner(System.in);
        String info2 =scan.nextLine();
        suffix suffix = new suffix();
        String info1 = suffix.getAnswer(info2);//转后缀
        System.out.println("后缀:"+info1);
        System.out.println();
        System.out.println("密钥:");
        // 读取对方的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);
        //生成共享信息
        byte[ ] sb=ka.generateSecret();
        for(int i=0;i<sb.length;i++){
            System.out.print(sb[i]+",");
        }
        SecretKeySpec k=new SecretKeySpec(sb,0,24,"AES");//生成密钥
        Cipher cp=Cipher.getInstance("AES");
        cp.init(Cipher.ENCRYPT_MODE, k);
        byte ptext[]=info1.getBytes("UTF8");
        for(int i=0;i<ptext.length;i++){
            System.out.print(ptext[i]+",");
        }
        System.out.println("");
        byte ctext[]=cp.doFinal(ptext);
        for(int i=0;i<ctext.length;i++){
            System.out.print(ctext[i] +",");
        }
        System.out.println();
        FileOutputStream f3=new FileOutputStream("SEnc.dat");//密文加密
        f3.write(ctext);
        String x=info1;
        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);
        }
        System.out.println("明文的MD5值:"+result);
        String ai ="";
        for (int i=0;i<ctext.length;i++)
        {
            ai+=ctext[i]+" ";
        }
        System.out.println("密文:"+ai);
        outputStreamWriter.write(ai+"\n"+result);//传输密文
        outputStreamWriter.flush();
        socket.shutdownOutput();
        //接收服务器的响应
        String reply = null;
        reply = bufferedReader.readLine();
        System.out.println("结果:"+reply);
        //4.关闭资源
        bufferedReader.close();
        inputStream.close();
        //printWriter.close();
        outputStream.close();
        socket.close();
    }
}

我们完成的实验的码云链接:实验五
运行的结果截图:

3. 实验过程中遇到的问题和解决过程

  • 问题1:进行第三个实验时,我们遇到了一些问题,即将字节型数组的密文转换成String类型时,传输过来时,总是会出现乱码,结果导致没有办法解密。

  • 问题1解决方案:这里显示的密文是不合法的,我们在这里卡了很久,最后采用的是不再进行转换成String类型的转换,我们直接将密文的byte数组使用字节流传输,接收时使用一个byte的数组进行接收,这里就没有问题了,具体代码是客户端是outputStream.write(ctext);outputStream.flush();,服务器是byte[] ctext = inputStream.readAllBytes();,最后就是这样,将密文传输过来,并完成后续步骤。

  • 问题2:实验四时,我们一开始采用的用DH算法对DES加密进行产生密文,但是在加密时,这里的始终是DH算法产生的密钥格式存在问题,问题具体见图:

  • 问题2解决方案:最后我们采用了伙伴提出的使用AES进行加密,但是这里也遇到了些问题,依旧加密过程存在问题,询问了余坤澎他们小组,对一处进行修改后完成,修改的内容为由SecretKeySpec k=new SecretKeySpec(sb,"AES");改为SecretKeySpec k=new SecretKeySpec(sb,0,24,"AES");,通过查询API文档,我们对这里有了一定了解。

  • 问题3:实验5中我们将byte数组的密文直接转换成了String类型,传输过去就会出现如下图这样的一串应密文的数字格式:

  • 问题3解决方案:把两条数据都写在一个输出语句中。但这样就有两种做法:一是把字节型的密文转换成字符串类型;二是把字符串类型的MD5值写进存放密文的字节数组。我们选择了前者,但是导致的问题就是如问题5所叙述的那样,客户端输出的结果是正确无误的,但是传到服务器端之后,显示出来的就是如上图所示,这个我们实在是没有一点办法,完全也不懂,最后我们的解决方案是将byte数组中每个元素取出,写入一个String字符串中,然后接收这个字符串后将逐个拿出放入byte数组中,从而实现了密文的传递。

其他(感悟、思考等)

  • 本周的进行的实验,也是Java程序设计的学习的尾声了,一个学期下来学会的不少,但没学会的也有很多。这次的实验有些懈怠,自己没有太多的去想遇到的问题的解决办法,希望下个学期能拿出更加认真的态度去学习每个知识点,遇到的问题也要更多的去自己去想解决办法。

参考资料

posted @ 2018-06-19 17:29  ◕‿◕  阅读(262)  评论(0编辑  收藏  举报
© 2018 GitHub, Inc.