电子公文系统
个人贡献值
本人对于此项目的贡献是文件传输和用户口令的保护,及负责整个系统密码安全
实践过程
首先创建数据库连接用户名密码
进入mysql本地
创建自己用户名(20201325xjr)
为“20201325xjr”授全部权限
查看数据库用户是否添加成功
我们发现已经创建成功啦!
修改idea项目通过20201325xjr连接数据库
就成功啦!!!
项目中信息保护方法
此电子公文系统采用的是调用openssl算法库中的指令对数据进行加解密。首先我们来看一下项目crypto层的openssl类
crypto.Openssl
该项目的文件传输使用SM4对称加密,密钥分配通过使用rand生成16字节的随机数,用户密码使用SM3算法计算摘要值。部分代码如下:
SM3
public static String SM3(String data) throws IOException {
List<String> commandArr = new ArrayList<>();
commandArr.add("/bin/sh");
commandArr.add("-c");
String cmd = "echo "+data+" | openssl sm3";
commandArr.add(cmd);
String result = RunCmd.run(commandArr.toArray(new String[commandArr.size()]));
return result.substring(9);
}
SM4
public static void SM4encrypt(String filePath,String key) throws IOException {
String cmd = "openssl sm4 -in " + filePath + " -out " + filePath + ".en -K " + key + " -iv " + key;
RunCmd.run(cmd);
}
public static void SM4decrypt(String filePath,String key) throws IOException {
String cmd = "openssl sm4 -d -in " + filePath + ".en -out " + filePath + " -K " + key + " -iv " + key;
RunCmd.run(cmd);
}
rand16
public static String rand16() throws IOException {
//生成16字节的随机数
String cmd = "openssl rand -hex 16";
String result = RunCmd.run(cmd);
return result;
}
由代码我们可以看出使用的是openssl命令行指令对数据进行加密解密。通过创建cmd变量存储待加密的文件路径和密钥,然后传入RunCmd类中的run方法进行执行。
dao.UserDao
我们通过看项目dao层的代码来看用户的口令保护程序
注册用户
可以看到用户在页面进行交互后传入的密码要先通过Openssl中的SM3方法之后再被放入sql字符串中进行insert。
然后通过PreparedStatement类的setString方法进行传入参数,其中每个用户自己的对称密钥通过Openssl中的rand16方法进行生成
参数值与数据库对应关系如下:
用户登录
可以看到用户在登录的时候会首先通过select语句进行寻找用户名,找到用户名之后通过用户名寻找其id、name、password、level,随后通过if判断密码摘要值来决定用户身份。
上传公文controller.UploadServlet
我们在controller层的UploadServlet类中添加加密传输代码
加密核心代码
//使用用户的对称密钥对文件加密
Openssl.SM4encrypt(path+filePath, u.getEncrykey());
//删除明文
Openssl.deletePlain(path+filePath);
上传公文 UploadServlet(xjr发给tsx)
定义存储路径
path为服务器存储的公文地址
获取上传文件名
- 首先通过req.getPart方法获取文件存入part对象中
Part part = req.getPart("file");
- 随后定义disposition变量获取文件的路径及名字
String disposition = part.getHeader("Content-Disposition");
- 使用substring方法获取路径中最后的那个文件名,如:*.txt
String realFileName = disposition.substring(disposition.lastIndexOf("=\"") + 2, disposition.length()-1);
- 随后使用InputStream类定义一个对象is并用getInputStream方法获取part中文件的数据流
InputStream is = part.getInputStream();
- 动态获取服务器路径
此处使用了UUID.randomUUID().toString方法。
String filePath = String.format("/%s/%s", UUID.randomUUID().toString(), realFileName);
我们尝试用System.out.println输出filePath看看如下:
此处我上传了一个1.txt,前面路径是服务器当前的路径
- 获取文件
使用Path类定义一个file变量,并使用get方法获取文件
Path file = Paths.get(path, filePath);
- 输出文件流
使用FileOutputStream类创建对象fos进行输出文件流
FileOutputStream fos = new FileOutputStream(file.toFile());
byte[] bty = new byte[1024];
int length =0;
while((length=is.read(bty))!=-1){
fos.write(bty,0,length);
}
- 文件加密
此处使用了Openssl.SM4encrypt方法对文件进行加密,注意:此处加密文件路径为path+filePath,即/home/xjr/桌面/Project2/file/服务器动态路径/filename
Openssl.SM4encrypt(path+filePath, u.getEncrykey());
对照刚刚的System.out.println输出的filePath结果我们看看是否生成对应文件目录
可以发现已经成功!
下载公文controller.DownServlet
解密核心代码
Openssl.SM4decrypt(path,key);
下载公文过程
- 获取客户端需要下载的文件名
此处创建了一个file字符串存取数据库动态地址+文件名
String file = request.getParameter("file");
我们可以使用System.out.println(file);
对其验证:
- 获取文件绝对地址
定义一个path字符串,存储项目路径+服务器动态路径+文件名
String path = UploadServlet.path+"/"+file; //默认认为文件在当前项目的根目录
- 获取发送者信息
定义字符串sender存储发送者名字,并使用documentService类中的findDocumentByPath和getSendUser方法进行查找发送者名字。
String sender = documentService.findDocumentByPath(file).getSendUser();
- 获取解密密钥
由于使用的是SM4算法,此算法为一个对称密码算法,故需要将获取发送方的密钥。
这里使用了userService类中的getEncrykey方法;
key = userService.findUserByName(sender).getEncrykey();
这里我们用xjr用户给tsx用户发送公文,随后tsx下载公文为例,用System.out.println(key);
查看key值
- 解密
此处使用了Openssl.SM4decrypt方法进行解密
Openssl.SM4decrypt(path,key);
将目标文件解密后得到: