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

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


实验步骤

(一)网络编程与安全-1

实验要求:

两人一组结对编程:java

  1. 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA
  2. 结对实现中缀表达式转后缀表达式的功能 MyBC.java
  3. 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
  4. 上传测试代码运行结果截图和码云连接git

    知识点:

    1.栈的一个应用是用来对四则运算表达式进行求值。算法

表达式Exp = S1 + OP + S2(S1 ,S2是两个操做数,OP为运算符)有三种标识方法:编程

  • OP + S1 + S2 为前缀表示法数组

  • S1 + OP + S2 为中缀表示法安全

  • S1 + S2 + OP 为后缀表示法服务器

前缀式: + * a b * - c / d e f网络

中缀式: a * b + c - d / e * fsocket

后缀式: a b * c d e / - f * +
能够看出:
- 操做数之间的相对次序不变; - 运算符的相对次序不一样; - 中缀式丢失了括弧信息,导致运算次序不肯定; - 前缀式的运算规则为:连续出现的两个操做数和在它们以前且紧靠它们的运算符构成一个最小表达式; - 后缀式的运算规则为:运算符在式中出现的顺序恰为表达式的运算顺序;每一个运算符和在它以前出现且紧靠它的两个操做数构成一个最小表达式。
后缀表示法是波兰逻辑学家J.Lukasiewicz于1929年提出的,又叫作逆波兰表达式。
Linux命令dc能够用来对逆波兰式表达式进行求值,dc的打印类命令:p:打印栈顶元素并换行

n: 打印栈顶元素并将其弹出栈,完毕后不换行

P: putchar ( int(栈顶元素) % 256) 并弹栈顶,不换行

f: 从栈顶至栈底打印栈中全部值,每一个一行

dc的运算符:

+: 依次弹出w1与w2,将w2+w1压栈。精度为结果值精度

-: 依次弹出w1与w2,将w2-w1压栈

*: 依次弹出w1与w2,将w2*w1压栈。精度为结果值精度与precision中较大值

/ : 依次弹出w1与w2,将w2/w1压栈。精度为precision

%: 依次弹出w1与w2,将w2-w2/w1*w1压栈

~: 依次弹出w1与w2,依次将w2/w1与w2%w1压栈

^ : 依次弹出w1与w2,将w2^((int)w1)压栈。精度为w2精度与precision中较大值

| : 依次弹出w1 w2与w3,将 w3 ^ ((int)w2) (mod w1) 压栈。w1 w3 需为整数

v: 弹出w1,将sqrt(v)压栈。精度为precision

dc支持栈操做:

c : 清空栈

d : 将栈顶元素复制并压栈

r : 交换栈顶两元素 XXX
咱们如何实现dc? 这要用到栈。对逆波兰式求值时,不须要再考虑运算符的优先级,只需从左到右扫描一遍后缀表达式便可。求值伪代码以下:
- 设置一个操做数栈,开始栈为空; - 从左到右扫描后缀表达式,遇操做数,进栈; - 若遇运算符,则从栈中退出两个元素,先退出的放到运算符的右边,后退出的放到运算符左边,运算后的结果再进栈,直到后缀表达式扫描完毕。此时,栈中仅有一个元素,即为运算的结果。
Linux中另一个计算器bc就是用来计算中缀表达式的:

如何由中缀式求得后缀式?

由中缀式求得后缀式可使用栈,伪代码以下:
- 设立一个栈,存放运算符,首先栈为空; - 从左到右扫描中缀式,若遇到操做数,直接输出,并输出一个空格做为两个操做数的分隔符; - 若遇到运算符,则与栈顶比较,比栈顶级别高则进栈,不然退出栈顶元素并输出,而后输出一个空格做分隔符; - 若遇到左括号,进栈;若遇到右括号,则一直退栈输出,直到退到左括号止。 - 当栈变成空时,输出的结果即为后缀表达式。

实验代码:

  • 中缀转后缀:
import java.util.Stack;

public class MyBC {
    MyBC(){}
    public static String infixToSuffix(String exp){
        Stack<String> s = new Stack<String>();         // 建立操做符堆栈
        String suffix = "";            // 要输出的后缀表达式字符串
        String suffix1 = "";             //上一次的后缀表达式
        String suffix2 = "";
        String str[] = exp.split(" ");
        int length = str.length; // 输入的中缀表达式的长度
        String temp="";
        for (int i = 0; i < length; i++) {            // 对该中缀表达式的每个字符并进行判断
            switch (str[i]) {
                case " ":break;           // 忽略空格
                case "(":
                    s.push(str[i]);                  // 若是是左括号直接压入堆栈
                    break;
                case "+":
                case "-":
                    if(s.size() != 0){          // 碰到'+' '-',将栈中的全部运算符所有弹出去,直至碰到左括号为止,输出到队列中去
                        temp = s.pop();
                        if (temp.equals("(")) {     // 将左括号放回堆栈,终止循环
                            s.push(temp);
                            s.push(str[i]);
                            break;
                        }
                        else{
                            s.push(str[i]);
                            suffix2 = suffix2 + temp + " ";
                            break;
                        }
                    }
                    else{
                        s.push(str[i]);      // 说明是当前为第一次进入或者其余前面运算都有括号等状况致使栈已经为空,此时须要将符号进栈
                        break;
                    }
                    // 若是是乘号或者除号,则弹出全部序列,直到碰到加好、减号、左括号为止,最后将该操做符压入堆栈
                case "*":
                case "÷":
                    if(s.size()!=0){
                        temp = s.pop();
                        if(temp.equals("+")||temp.equals("-")||temp.equals("(")){
                            s.push(temp);
                            s.push(str[i]);
                            break;
                        }
                        else{
                            s.push(str[i]);
                            suffix2 = suffix2+temp+" ";
                            break;
                        }
                    }
                    else {
                        s.push(str[i]);     //当前为第一次进入或者其余前面运算都有括号等状况致使栈已经为空,此时须要将符号进栈
                        break;
                    }
                    // 若是碰到的是右括号,则距离栈顶的第一个左括号上面的全部运算符弹出栈并抛弃左括号
                case ")":
                    while (!s.isEmpty()) {
                        temp = s.pop();
                        if (temp.equals("(")) {
                            break;
                        } else {
                            suffix2 = suffix2+temp+" ";
                        }
                    }
                    break;
                // 默认状况,若是读取到的是数字,则直接送至输出序列
                default:
                    suffix2 = suffix2+str[i]+" ";
                    break;
            }

        }
        // 若是堆栈不为空,则把剩余运算符一次弹出,送至输出序列
        while (s.size() != 0) {
            suffix2 = suffix2+s.pop()+" ";
        }
        if(suffix1.equals("")){          //第一个题目
            suffix1 = suffix2;
            suffix = suffix2;
        }
        else{
            if(suffix2.equals(suffix1))
                suffix = "";
            else
                suffix = suffix2;
        }
        suffix1 = suffix2;
        return suffix;
    }
}

运行截图:

(二)网络编程与安全-2

实验要求:

结对编程:1人负责客户端,一人负责服务器

  1. 注意责任归宿,要会经过测试证实本身没有问题
  2. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
  3. 客户端让用户输入中缀表达式,而后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式经过网络发送给服务器
  4. 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  5. 客户端显示服务器发送过来的结果
  6. 上传测试结果截图和码云连接

知识点:

  • 网络套接字是基于TCP协议的有链接通讯,套接字链接就是客户端的套接字对象和服务器端的套接字对象经过输入流、输出流链接在一块儿。服务器创建ServerSocket对象,ServerSocket对象负责等待客户端请求创建套接字链接,而客户端创建Socket对象服务器发出套接字链接请求。
  • 基于UDP的通讯和基于TCP的通讯不一样,基于UDP的信息传递更快,但不提供可靠性保证。

    实验代码:

  • 服务器端:
package 实验五.Assign2;

import 实验五.Assign1.MyDC;

import java.io.*;
import java.net.*;
public class Sever {
    public static void main(String[] args) {
        ServerSocket serverForClient = null;
        Socket socketOnServer = null;
        DataOutputStream out = null;
        DataInputStream in = null;
        MyDC myDC = new MyDC();
        try{
            serverForClient = new ServerSocket(2010);
        }catch (IOException e1){
            System.out.println("我是猪,我又饿了"+e1);
        }
        try{
            System.out.println("等待小猴投喂");
            socketOnServer = serverForClient.accept();
            out = new DataOutputStream(socketOnServer.getOutputStream());
            in = new DataInputStream(socketOnServer.getInputStream());
            String suffix = in.readUTF();
            System.out.println("胖砾收到小猴的提问"+suffix);
            out.writeUTF(myDC.evaluate(suffix)+"");
            Thread.sleep(500);
        }catch (Exception e){
            System.out.println("小猴已断开链接");
        }
    }
}

运行结果:我负责服务器端

(三)网络编程与安全-3

实验要求:

加密结对编程:1人负责客户端,一人负责服务器

  1. 注意责任归宿,要会经过测试证实本身没有问题
  2. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
  3. 客户端让用户输入中缀表达式,而后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后经过网络把密文发送给服务器
  4. 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,能够用数组保存),而后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  5. 客户端显示服务器发送过来的结果
  6. 上传测试结果截图和码云连接

    知识点:

  • DES密钥
    获取密钥生成器,KeyGenerator kg=KeyGenerator.getInstance("DESede")
    初始化密钥生成器,kg.init(168)
    生成密钥,SecretKey k=kg.generateKey( )
  • 服务端DES解密
    获取密钥长度,String keylength = in.readUTF()
    获取密钥
    byte []kb = new byte[Integer.parseInt(keylength)]; for(int i=0; i<Integer.parseInt(keylength); i++){ String t = in.readUTF(); kb[i] = Byte.parseByte(t); }
    获取密文长度,String clength = in.readUTF()
    获取密文
    byte []ctext = new byte[Integer.parseInt(clength)]; for(int i=0; i<Integer.parseInt(clength); i++){ String temp = in.readUTF(); ctext[i] = Byte.parseByte(temp); }
    建立密码器(Cipher对象)Cipher cp=Cipher.getInstance("DESede")
    初始化密码器cp.init(Cipher.DECRYPT_MODE, k)
    执行解密byte []ptext=cp.doFinal(ctext)

    实验代码:服务器端

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.*;
public class Sever1 {
    public static void main(String[] args) {
        ServerSocket serverForClient = null;
        Socket socketOnServer = null;
        DataOutputStream out = null;
        DataInputStream in = null;
        MyDC myDC = new MyDC();
        try{
            serverForClient = new ServerSocket(2010);
        }catch (IOException e1){
            System.out.println("我是猪,我又饿了"+e1);
        }
        try{
            System.out.println("等待小猴投喂");
            socketOnServer = serverForClient.accept();
            out = new DataOutputStream(socketOnServer.getOutputStream());
            in = new DataInputStream(socketOnServer.getInputStream());
            String keylength = in.readUTF();
            byte []kb = new byte[Integer.parseInt(keylength)];
            for(int i=0; i<Integer.parseInt(keylength); i++){
                String t = in.readUTF();
                kb[i] = Byte.parseByte(t);
            }
            String clength = in.readUTF();
            byte []ctext = new byte[Integer.parseInt(clength)];
            for(int i=0; i<Integer.parseInt(clength); i++){
                String temp = in.readUTF();
                ctext[i] = Byte.parseByte(temp);
            }
            SecretKeySpec k=new  SecretKeySpec(kb,"DESede");
            Cipher cp=Cipher.getInstance("DESede");
            cp.init(Cipher.DECRYPT_MODE, k);
            byte []ptext=cp.doFinal(ctext);
            String suffix = new String(ptext,"UTF8");
            System.out.println("胖砾收到小猴的提问"+suffix);
            out.writeUTF(myDC.evaluate(suffix)+"");
            Thread.sleep(500);
        }catch (Exception e){
            System.out.println("小猴已断开链接");
        }
    }
}

实验结果:

参考:Java密码学算法

(四)网络编程与安全-4

实验要求:

密钥分发结对编程:1人负责客户端,一人负责服务器

  1. 注意责任归宿,要会经过测试证实本身没有问题
  2. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
  3. 客户端让用户输入中缀表达式,而后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密经过网络把密文发送给服务器
  4. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
  5. 服务器接收到后缀表达式表达式后,进行解密,而后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  6. 客户端显示服务器发送过来的结果
  7. 上传测试结果截图和码云连接

知识点:

执行密钥协定的标准算法是DH算法(Diffie-Hellman算法)。

  • 建立DH公钥和私钥
    DH算法是创建在DH公钥和私钥的基础上的, A须要和B共享密钥时,A和B各自生成DH公钥和私钥,公钥对外公布而私钥各自秘密保存,在初始化时须要为DH指定特定的参数。

程序最后将公钥和私钥以对象流的形式保存在文件中,文件名经过命令行参数指定,第一个命令行参数对应的文件保存公钥,第二个命令行参数对应的文件保存私钥。

运行程序:

创建两个目录A和B,模拟须要秘密通讯的A、B双方,因为DH算法须要A和B各自生成DH公钥和私钥,所以在这两个目录下都拷贝编译后文件Key_DH。

首先由A建立本身的公钥和私钥,即在A目录下输入“java Key_DH Apub.dat Apri.dat”运行程序,这时在目录A下将产生文件Apub.dat和Apri.dat,前者保存着A的公钥,后者保存着A的私钥。
而后由B建立本身的公钥和私钥,即在B目录下输入“java Key_DH Bpub.dat Bpri.dat”运行程序,这时在目录B下将产生文件Bpub.dat和Bpri.dat,前者保存着B的公钥,后者保存着B的私钥。
最后发布公钥,A将Apub.dat拷贝到B目录,B将Bpub.dat拷贝到A的目录。
这样,A、B双方的DH公钥和私钥已经建立并部署完毕。

  • 建立共享密钥
    DH算法中,A能够用本身的密钥和B的公钥按照必定方法生成一个密钥,B也能够用本身的密钥和A的公钥按照必定方法生成一个密钥,因为一些数学规律,这两个密钥彻底相同。这样,A和B间就有了一个共同的密钥能够用于各类加密。

Java中KeyAgreement类实现了密钥协定,它使用init( )方法传入本身的私钥,使用doPhase( )方法传入对方的公钥,进而可使用generateSecret( )方法生成共享的信息具体步骤以下:

(1) 读取本身的DH私钥和对方的DH公钥

(2) 建立密钥协定对象

(3) 初始化密钥协定对象

(4) 执行密钥协定

(5) 生成共享信息

程序最后将共享信息打印了出来,以便直观地对比A和B获得的信息是否相同。将程序KeyAgree编译后分别拷贝在A和B两个目录,首先在A目录输入“java KeyAgree Bpub.dat Apri.dat”运行程序,它使用文件Bpub.dat中对方的公钥和文件Apri.dat中本身的私钥建立了一段共享的字节数组。

实验代码:服务器端

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.*;
public class Server4 {
    public static void main(String[] args) {
        ServerSocket serverForClient = null;
        Socket socketOnServer = null;
        DataOutputStream out = null;
        DataInputStream in = null;
        MyDC myDC = new MyDC();
        try{
            serverForClient = new ServerSocket(2010);
        }catch (IOException e1){
            System.out.println("我是猪,我又饿了"+e1);
        }
        try{
            System.out.println("等待小猴投喂");
            socketOnServer = serverForClient.accept();
            out = new DataOutputStream(socketOnServer.getOutputStream());
            in = new DataInputStream(socketOnServer.getInputStream());
            //获取密文
            String clength = in.readUTF();
            byte []ctext = new byte[Integer.parseInt(clength)];
            for(int i=0; i<Integer.parseInt(clength); i++){
                String temp = in.readUTF();
                ctext[i] = Byte.parseByte(temp);
            }
            //获取密钥
            String keylength = in.readUTF();
            byte []ckey = new byte[Integer.parseInt(keylength)];
            for(int i=0; i<Integer.parseInt(keylength); i++){
                String temp = in.readUTF();
                ckey[i] = Byte.parseByte(temp);
            }
            //密钥解密
            SecretKeySpec k1 = KeyAgree.KeyAgree("Clientpub.dat","Serverpri.dat");
            Cipher cp=Cipher.getInstance("DESede");
            cp.init(Cipher.DECRYPT_MODE, k1);
            byte []pkey=cp.doFinal(ckey);
            //密文解密
            SecretKeySpec k=new  SecretKeySpec(pkey,"DESede");
            cp.init(Cipher.DECRYPT_MODE, k);
            byte []ptext=cp.doFinal(ctext);
            String suffix = new String(ptext,"UTF8");
            System.out.println("胖砾收到小猴的提问"+suffix);
            out.writeUTF(myDC.evaluate(suffix)+"");
            Thread.sleep(500);
        }catch (Exception e){
            System.out.println("小猴已断开链接");
        }
    }
}

实验结果:

(五)网络编程与安全-5

实验要求:

完整性校验结对编程:1人负责客户端,一人负责服务器

  1. 注意责任归宿,要会经过测试证实本身没有问题
  2. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
  3. 客户端让用户输入中缀表达式,而后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密经过网络把密文和明文的MD5値发送给服务器
  4. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
  5. 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  6. 客户端显示服务器发送过来的结果
  7. 上传测试结果截图和码云连接

    知识点:

    Java摘要算法- MD5:使用Java计算指定字符串的消息摘要。java.security包中的MessageDigest类提供了计算消息摘要的方法,首先生成对象,执行其update()方法能够将原始数据传递给该对象,而后执行其digest( )方法便可获得消息摘要。具体步骤以下:

(1) 生成MessageDigest对象
MessageDigest m=MessageDigest.getInstance("MD5");

(2) 传入须要计算的字符串
m.update(x.getBytes("UTF8" ));

(3) 计算消息摘要
byte s[ ]=m.digest( );

(4) 处理计算结果
必要的话可使用以下代码将计算结果s转换为字符串。
String result=""; for (int i=0; i<s.length; i++){ result+=Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6); }

实验代码:

MessageDigest m = MessageDigest.getInstance("MD5");
            m.update(suffix.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);
            }

实验结果:

码云连接

实验感想:

本次实验是对以前的知识点的应用,实验涉及的内容主要是网络编程方面的,感受难度比较大,不少东西都不是很理解。经过和结对伙伴的共同研究,不断查阅资料,才最终完成实验,收获仍是很大的。

相关文章
相关标签/搜索