网络安全实验:掌握Socket与密码技术,设计安全传输系统及结对分工
20145213 这门《Java 程序设计》有实验五,内容是 Java 网络编程及安全。

1.掌握Socket程序的编写。
2.掌握密码技术的使用。
3.设计安全传输系统。
实验预期
1.客户端与服务器连接
客户端输入明文,接着利用 DES 算法进行加密。DES 的密钥是用 RSA 公钥密码中服务器的公钥来加密的。然后计算明文的 Hash 函数值,最后将明文的 Hash 函数值与加密后的明文一起传送给客户端。
客户端利用 RSA 公钥密码来解密服务器的私钥所对应的密钥,然后用该密钥对密文进行解密,从而得出明文。接着计算得出明文的 Hash 函数值,查验其是否与传送过来的一致。倘若一致,就表明匹配成功。
结对实验
我负责客服端这一部分。我的搭档是 20145228 江苒,她负责服务端部分。
实验准备
首先要对 Java 网络编程的基本知识有所了解。娄老师推荐的博客能够对其进行准确且详细的阐述,在此就不再赘述了。
2.下载娄老师发在群里的代码压缩包。
在理解的基础上进行娄老师代码的组装与编写。我不太擅长,总是学不会。要更聪明一些,记得自我保护。在必要的时候向学长要了客户端的代码,如下:
import javax.crypto.Cipher;
引入 javax.crypto.spec.SecretKeySpec 类。
import java.io.*;
import java.math.BigInteger;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.Key;
引入 Java 中的安全类 MessageDigest 。
导入 Java 的安全接口中的 RSAPrivateKey 类。
public class Server {
公共静态的 void 主函数(String 参数数组[])抛出异常。
ServerSocket link = null;
Socket socket = null;
try {
创建服务器套接字,链接为 new ServerSocket(2030);
输出的内容为:端口号加上 link 的本地端口号。具体来说,就是将 link 的本地端口号获取出来后与“端口号:”进行拼接并输出。
系统正在输出“服务器已经启动...”这句话。
socket 等于 link 所接受的内容;这是在等待客户端进行连接。
System.out.println("已经建立连接");
//获得网络输入流对象的引用
创建一个 BufferedReader 对象 in ,它是通过将一个 InputStreamReader 对象与 socket 的输入流 socket.getInputStream() 关联起来而创建的。
//获得网络输出流对象的引用
创建一个 PrintWriter 对象 out ,它是通过将一个 BufferedWriter 对象包装在一个 OutputStreamWriter 对象中,并将该 OutputStreamWriter 对象的输出流与 socket 的输出流关联起来创建的。同时,将 autoFlush 参数设置为 true ,以便在每次调用 println 、 printf 或 format 方法时自动刷新输出缓冲区。
// 使用服务器端RSA的私钥对DES的密钥进行解密
String line = in.readLine();
创建了一个名为 cipher 的 BigInteger 对象,这个对象是通过将 line 转换而来的。
创建一个 FileInputStream 对象 f,将其指向名为 "Skey_RSA_priv.dat" 的文件。
创建一个名为 b 的 ObjectInputStream 对象,该对象通过传入的 f 进行初始化。
RSAPrivateKey 被赋值为从 b 读取的对象并转换为 RSAPrivateKey 类型,即 prk = (RSAPrivateKey) b.readObject();
BigInteger d;d 等于 prk 获取的私钥指数;d 是通过 prk 获取到的私钥指数;prk 获取到的私钥指数就是 d ;从 prk 中获取到的私钥指数为 d ;prk 获取到的私钥指数被赋值给 d ;d 由 prk 获取的私钥指数所确定
prk 获取到的模数值即为 BigInteger n 。
m 等于 d 对 n 取模的结果。
System.out 会打印出 "d= " 以及变量 d 的值。
System 输出打印出 "n= " 与 n 进行拼接后的内容。
System.out.println("m 等于 " + m);
m 被转换成字节数组,这个字节数组是 keykb 。
// 使用DES对密文进行解密
读取客户端传送来的数据,将其赋值给字符串 readline,即 readline = in.readLine();
创建了一个名为 f2 的 FileInputStream 对象,用于读取名为"keykb1.dat"的文件。
int num2 = f2.available();
创建了一个名为 k 的 Key 对象,该对象是通过使用 keykb 作为密钥字节数组,以及 "DESede" 作为加密算法来生成的 SecretKeySpec 对象。
Cipher 被实例化为 cp,并且 cp 是通过 Cipher.getInstance("DESede") 来创建的。
cp 初始化了 Cipher 的解密模式,并使用了 k 。
cp 对 ctext 执行 doFinal 操作后,得到的结果是 byte[] 类型的 ptext。
创建一个新的字符串 p,该字符串是通过将 ptext 按照“UTF8”编码进行转换得到的。
从客户端接收到的信息为:p;然后打印了解密结果;具体是通过 System.out.println 来实现的
// 使用Hash函数检测明文完整性
读取一行输入并赋值给字符串 aline3 。
String x = p;
使用 MD5 算法可以返回实现指定摘要算法的 MessageDigest 对象,即 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);
if (aline3.equals(result)) {
System.out.println("匹配成功");
}
out.println("匹配成功");
out.close();
in.close();
link.close();
} catch (Exception e) {
System.out.println(e);
}
}
//二进制转换成十六进制,防止byte[]数字转换成string类型时造成的数据损失
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());//将字符串中的小写字母转换成大写字母,然后加在字符串上
}
return sb.toString();
}
//将十六进制转换为二进制
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1),16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2),16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
实验步骤
有了上述的实验准备后,再参考周围同学的实验报告,实验做起来就很顺利。非常舒服,在此要感谢一些为实验做基础工作的人,就像“前人栽树后人乘凉”所说,因为有你们,才有现在的顺利进行。
首先要达成两台计算机之间的连接。为了能够便捷地辨认网络上的每一个设备,网络中的每一个设备都会拥有一个独一无二的数字标识,这个标识就是 IP 地址。我们可以在命令行通过输入 ipconfig 指令来获取 IP 地址。如图:
彼此把 IPv4 地址告诉搭档之后,在命令行使用 ping 指令来进行链接。
遇到这种状况别慌张,多尝试几次。倘若依然失败,可考虑更换网络。
接下来是实验中最难的部分,那就是编写各自负责的端口代码并且让其成功运行。但是我们在实验准备阶段已经偷懒了,因为拿到学长的代码后直接进行编译运行就可以了。如下:
因为使用了 RSA 的公钥文件,然而却忘记进行拷贝。将 Skey_RSA_pub.dat 文件拷贝到工程中与 src 处于同一级别的目录下就可以了。因为我所处的是客户端,如果是服务端的话,就应当将 Skey_RSA_pri.dat 文件拷贝到工程中与 src 处于同一级别的目录下就可以了。
4.重新运行一次,客户端成功界面如下:
服务端成功界面如下:
心得体会
我曾看到我舍友花费 5 个小时,和他的伙伴钻研代码的实现以及与计算机的连接。那时候没有成功同学的实验报告,他们可以说是先行者。老师实验五的链接点开后是实验楼的登录界面,也不知道有什么深意。实验的难度是可以想象到的。也许冥冥之中注定,含泪播种的人最终能够含笑收获。当他们实验成功的那一瞬间,作为旁观者的我能够感受到他们灵魂深处的喜悦。我当时的想法是,这还算正常吧,毕竟实验成功谁会不开心呢?然而,当我在大家差不多都提交实验博客的时候,参考了各种以往的经验教训,一路顺利且有惊无险地完成实验的那一瞬间,却并没有预想到的喜悦,取而代之的是一种完成任务后的轻松感。这次实验代码我偷懒了。娄老师发的实验链接是很好的教材,堪称教材鸡汤。但即使给我一桶这样的量,我也表示消化不良。
PSP(个人软件流程)的时间以及步骤所耗费的时间占比情况如下:步骤耗时百分比与 PSP 时间相关联。
需求分析
30min
20%
设计
30min
20%
代码实现
10min(非原创)
6%
测试
60min
40%
分析总结
20min
14%
参考资料
Java学习笔记(第8版)
《Java学习笔记(第8版)》学习指导