实验五《网络编程与安全》实验报告
实验步骤
(一)网络编程与安全-1
实验要求:
-
参考数据结构应用
-
结对实现中缀表达式转后缀表达式的功能 MyBC.java
-
结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
知识点
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 * f
后缀式: 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
就是用来计算中缀表达式的:
如何由中缀式求得后缀式?
由中缀式求得后缀式可以使用栈,伪代码如下:
- 设立一个栈,存放运算符,首先栈为空;
- 从左到右扫描中缀式,若遇到操作数,直接输出,并输出一个空格作为两个操作数的分隔符;
- 若遇到运算符,则与栈顶比较,比栈顶级别高则进栈,否则退出栈顶元素并输出,然后输出一个空格作分隔符;
- 若遇到左括号,进栈;若遇到右括号,则一直退栈输出,直到退到左括号止。
- 当栈变成空时,输出的结果即为后缀表达式。
运行结果
(二)网络编程与安全-2
实验要求:
结对编程:1人负责客户端,一人负责服务器
-
注意责任归宿,要会通过测试证明自己没有问题
-
基于Java Socket实现客户端/服务器功能,传输方式用TCP
-
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
-
服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
-
客户端显示服务器发送过来的结果
知识点
-
网络套接字是基于TCP协议的有连接通信,套接字连接就是客户端的套接字对象和服务器端的套接字对象通过输入流、输出流连接在一起。服务器建立ServerSocket对象,ServerSocket对象负责等待客户端请求建立套接字连接,而客户端建立Socket对象服务器发出套接字连接请求。
-
基于UDP的通信和基于TCP的通信不同,基于UDP的信息传递更快,但不提供可靠性保证。
遇到的问题及解决
8+2
3-1
等算式输出为0,错因是操作数和运算符之间要空格,如8 + 2
3 - 1
。
运行结果
- 客户端:
(三)网络编程与安全-3
实验要求:
加密结对编程:1人负责客户端,一人负责服务器
-
注意责任归宿,要会通过测试证明自己没有问题
-
基于Java Socket实现客户端/服务器功能,传输方式用TCP
-
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
-
服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
-
客户端显示服务器发送过来的结果
参考
步骤:
首先运行ServerReceive.java,输入 a * b + c - d / e * f
;再运行ClientSend.java,输入a * b + c - d / e * f
。
运行结果
-
服务器:
-
客户端:
(四)网络编程与安全-4
实验要求:
密钥分发结对编程:1人负责客户端,一人负责服务器
-
注意责任归宿,要会通过测试证明自己没有问题
-
基于Java Socket实现客户端/服务器功能,传输方式用TCP
-
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
-
客户端和服务器用DH算法进行3DES或AES算法的密钥交换
-
服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
-
客户端显示服务器发送过来的结果
知识点:
执行密钥协定的标准算法是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中自己的私钥创建了一段共享的字节数组。
(五)网络编程与安全-5
实验要求:
完整性校验结对编程:1人负责客户端,一人负责服务器
-
注意责任归宿,要会通过测试证明自己没有问题
-
基于Java Socket实现客户端/服务器功能,传输方式用TCP
-
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5值发送给服
务器 -
客户端和服务器用DH算法进行3DES或AES算法的密钥交换
-
服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
-
客户端显示服务器发送过来的结果
知识点
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);
}
运行程序
需求分析:
步骤 | 耗时 | 百分比 |
---|---|---|
需求分析 | 1h | 7% |
设计 | 1.5h | 48% |
代码实现 | 3h | 17% |
测试 | 3h | 14% |
分析总结 | 1h | 14% |