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

第一个任务

这个任务是要将输入的中缀表达式转换为后缀表达式,并用老师已经写好的后缀表达式进行计算,所以任务的难点就在于如何将中缀表达式转化为后缀表达式。对于这个难点,娄老师要求写成MyBC.java来进行操作。
因为第一次遇见这样的问题,所以果断转向百度求助,不出所料,有两篇博客,一篇为另一篇为
讲到了中缀表达式转后缀表达式的规则:
首先将各种运算符(包括括号)的优先级排列如下(数字越大,优先级越高):
1:(
2:+ -
3:* /
4:)
对输入的中缀表达式从左到右遍历:
1)如果遇到数字,直接添加到后缀表达式末尾;
2)如果遇到运算符+、-、*、/:
先判断栈是否为空。若是,则直接将此运算符压入栈。若不是,则查看当前栈顶元素。若栈顶元素优先级大于或等于此操作符级别,则弹出栈顶元素,将栈顶元素添加到后缀表达式中,并继续进行上述判断。如果不满足上述判断或者栈为空,将这个运算符入栈。要注意的是,经过上述步骤,这个运算符最终一定会入栈。
3)如果遇到括号:
如果是左括号,直接入栈。如果是右括号,弹出栈中第一个左括号前所有的操作符,并将左括号弹出。(右括号别入栈)。
4)字符串遍历结束后,如果栈不为空,则弹出栈中所有元素,将它们添加到后缀表达式的末尾,直到栈为空。

所以我就按照c++代码,写出了java的MyBC

 while(i<length){
            if(ch[i]>='0'&&ch[i]<='9'){
                st+=ch[i];
            }
            else if(ch[i]=='+'||ch[i]=='-'||ch[i]=='*'||ch[i]=='/'){
                if(stack.empty()){
                    temp = String.valueOf(ch[i]);
                    stack.push(temp);
                }
                else{
                    while (!stack.empty()){
                        tem=stack.peek().charAt(0);
                        if(getPriority(tem)>=getPriority(ch[i])){
                            st+=tem;
                            stack.pop();
                        }
                        else{
                            break;
                        }
                    }
                    temp = String.valueOf(ch[i]);
                    stack.push(temp);
                }
            }
            else{
                if(ch[i]=='('){
                    temp = String.valueOf(ch[i]);
                    stack.push(temp);
                }
                else{
                    while(!stack.peek().equals(String.valueOf('('))){
                        tem = stack.peek().charAt(0);
                        st+=tem;
                        stack.pop();
                    }
                    stack.pop();
                }
            }
            i++;
        }

但是出现了一些问题,就是在用MyDC的输入格式和MyBC输出格式不一样的情况。
因为没有注意到MyDC的输入的字符串中每个有效字符是由一个空格作为间隔的,所以在MyBC将中缀表达式转为后缀表达式的时候,并没有做处理,所以会导致最终MyDC计算结果为0的情况。
处理方法是在给st输出字符串添加元素的时候,多添加一个空格。
st+=tem;
替换为
st=st+tem+" ";

st+=ch[i];
替换为
st=st+ch[i]+" ";
就可以正确计算了。

第二个任务是

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

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

因为团队项目恰好也有关服务器和客户端,所以基于Java Socket实现客户端/服务器功能,传输方式用TCP对我来说,并不是很难。在这里我直接用的是java书上的最简单的例子。
然后在客户端增加输入功能,然后将输入的中缀表达式通过任务一的MyDC转换为MyBC,最终传输给服务器端,服务器端在接收到后缀表达式侯,进行MyBC的计算,最终得到答案。
但是还是出现了一个问题
java writeUtf出现了java.lang.NullPointerException
后来发现是忘记没有实例化~new DataOutputStream
增加

in = new DataInputStream(client.getInputStream());out = new DataOutputStream(client.getOutputStream());

之后就可以正常使用了。

第三个任务是

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

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

因为我之前已经写过DES的算法了,所以这次选的是AES算法挑战一下,但是出现了很多的问题。
javax.crypto.BadPaddingException: Given final block not properly padded
其中一个就是这个问题。
百度之后果然由大神已经解决了这个问题。
博客链接
和一篇文库

kgen.init(128, new SecureRandom(key.getBytes()));

替换为

kgen.init(128, random);

就可以了,具体问题不是很懂。

第四个任务是

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

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

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

  3. 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端

  4. 客户端显示服务器发送过来的结果

  5. 上传测试结果截图和码云链接

密钥交换算法——DH
简述
1976年,W.Diffie和M.Hellman在发表的论文中提出了公钥加密算法思想,但当时并没有给出具体的实施方案,原因在于没有找到单向函数(也就是消息摘要算法),但在该论文中给出了通信双方通过信息交换协商密钥的算法,即Diffie-Hellman密钥交换算法(简称为DH算法)。该算法的目的在于让消息的收发双方可以在安全的条件下交换密钥,以备后续加密/解密使用。因此,DH算法是第一个密钥协商算法,但仅能用于密钥分配,不能用于加密或者解密消息。

DH密钥交换算法的安全性基于有限域上的离散对数难题。基于这种安全性,通过DH算法进行密钥分配,使得消息的收发双方可以安全地交换一个密钥,再通过这个密钥对数据进行加密和解密处理。
其实该算法的原理和上一部分中简单乘法及其类似,只是获取da或者db不是简单的方程式了,而是涉及到对数运算。对数运算被认为是“难”的,这个难建立在目前为止没有找到一个快速计算对数的算法,数学上没有证明这个算法是否存在。
因为之前密码学数学基础可上讲过a^b mod n的快速算法,所以我就按照这个算法去计算。
public static int quick  (int a,int b,int m){
    int ans=a;
    String bin = binaryToDecimal(b);
    char[] chr = bin.toCharArray();
    for(int i=1;i<bin.length();i++){
        if(chr[i]==1){
            ans = (ans*ans*a)%m;
        }
        else {
            ans = (ans*ans)%m;
        }
    }
    return ans;
}
返回值便是a^b mod n。
还是按照socket方法,将生成的密钥作为下一个阶段AES的密钥,就可以保证接下来过程的正确性了。


第五个任务是

完整性校验结对编程:1人负责客户端,一人负责服务器
0. 注意责任归宿,要会通过测试证明自己没有问题

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

具体来说文件的MD5值就像是这个文件的“数字指纹”。每个文件的MD5值是不同的,如果任何人对文件做了任何改动,其MD5值也就是对应的“数字指纹”就会发生变化。比如下载服务器针对一个文件预先提供一个MD5值,用户下载完该文件后,用我这个算法重新计算下载文件的MD5值,通过比较这两个值是否相同,就能判断下载的文件是否出错,或者说下载的文件是否被篡改了。MD5实际上一种有损压缩技术,压缩前文件一样MD5值一定一样,反之MD5值一样并不能保证压缩前的数据是一样的。在密码学上发生这样的概率是很小的,所以MD5在密码加密领域有一席之地。但是专业的黑客甚至普通黑客也可以利用MD5值实际是有损压缩技术这一原理,将MD5的逆运算的值作为一张表俗称彩虹表的散列表来破解密码。
虽然MD5更多的是用在文件的校对上,但是也可以用在我们这个后缀表达式的信息上。
我的理解就是应该先用DH算法进行密钥的交换,生成双方一致的密钥,依此作为AES加密和解密的密钥。
MD5计算的是后缀表达式的值,保证后缀表达式的消息完整性。
因为我记得娄老师之前写过一篇密码学的博客,所以直接用的娄老师的代码。

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

![](https://img2018.cnblogs.com/blog/1613137/201905/1613137-20190531082507910-2019602431.png)
![](https://img2018.cnblogs.com/blog/1613137/201905/1613137-20190531082513816-964509183.png)

posted on 2019-05-31 08:24  20175307GSC  阅读(222)  评论(0编辑  收藏  举报

导航