基于保留格式加密技术的航班信息系统的设计思路和流程

这是我们在创新创业实践中一起完成的一个项目。本项目的名称是“基于保留格式加密技术的航班信息发布系统的设计与实现”,队伍中一共有四名成员,两名成员主要负责分析和完善算法,萌新学妹主要是学习经验,而我主要负责网站的搭建和系统功能的完善。下面说一下我具体的开发流程和思路。


航班数据库的建立

通过在网上搜集数据,我建立了存放旅客基本信息和航班信息的数据库。数据库由5个表构成,分别是:

  • city表,存放机场三字码对应的机场、城市以及省份
  • company表,存放航空公司代码及对应的航空公司名
  • passenger表,存放旅客信息。目前该表中的属性有姓名、年龄、电话号、身份证号、航班座位号、机场号
  • plane表,主要的航班信息表。存放起飞机场、到达机场、航空公司、航线代码、机型、时间日期等信息。
  • user表,与航班信息无关,主要是存放管理员的账号密码等信息

其中,只有passenger表和plane表需要加密存储,而city表和company表的作用只是为了对照信息,不存在需要加密的敏感信息。

保留格式加密算法的实现

这一部分主要由项目组另外两位成员完成,主要的目标就是在数据库与服务器进行通讯的过程中,将以对象的形式传递的航班信息进行加密解密。保证航班信息在数据库中是加密的,而在航班系统中以明文的形式展现。

什么是保留格式加密算法

在实际应用中,对数据库中的信用卡号、身份证号等敏感数据进行加密非常必要,然而使用传统分组密码通常会扩展数据,使数据长度和类型发生变化,需要修改数据库结构或应用程序来适应这些变化,成本非常高。此类加密要求密文与明文具有相同的格式,是一类新的加密问题,称为“保留格式加密(format-preserving encryption,简称FPE)”.

对于数据库敏感数据的保留格式加密,需要保证密文满足数据库对于数据格式的约束,主要包括:

  • 数据不能被扩充.例如,当加密N 位的数字时,必须输出另外一个N 位的数字
  • 数据类型不能被改变
  • 数据必须能被确定性地加密.对于数据库中作为主键或者索引字段的数据,被加密后将保留其所在的列作为主键或者索引的特性
  • 加、解密过程可逆

Feistel网络

Feistel 网络是目前主流的分组密码设计模式之一,基于Feistel 网络,可以通过定义分组大小、密钥长度、轮次数、子密钥生成、轮函数等来构造一个分组密码.在FPE 模型的构造过程中,Feistel 网络被广泛应用于构造合适分组长度的分组密码,其中,FFSEM 使用了平衡的Feistel 网络,FFX 模型中使用了两种非平衡Feistel 网络.本节总结了常见类型的Feistel 网络及其在不同FPE 模型中的应用。

常见的Feistel 网络类型主要包括传统的Feistel 网络、非平衡Feistel 网络、交互式Feistel 网络及以上3种的数值形式.

在Feistel 网络中,每一轮迭代的输出都保持了长度(对输入为字符串的Feistel 网络)或范围(对输入为整数
的Feistel 网络)不变,这一特点使其非常适合于处理FPE 问题。

算法选取与优化

通过参考论文,我们采用了三种保留格式加密算法

算法 功能 性能 安全性
FF1 用于单一格式加密。 10轮迭代,慢于FF3。 皆是在128bitAES的基础上实现的线性变换,同样安全可靠。
FF3 用于单一格式加密。 8轮迭代。 同上
IFX 用于混合格式加密,使用最为灵活。 可变轮次,算法复杂,实现最慢 同上

原则上,能用FF3就不用FF1,能用FF1就不用IFX。

优化扩展:

问题 解决方式
算法直接用于对数值的加密会有高位丢失的问题,高位丢失使得解密直接出错。 高位偏移策略
密文空间过小,不满足加密的前置条件。 前向扩充策略
对时间的加密有更为苛刻的格式要求 对数值的间接映射
随机因子T的有效选择 数据库的id号

综上所述,算法的目的就是可以将数据库中的各类的数据按照其原本的格式加密解密,防止黑客拖库的风险。

系统设计

本项目目的是设计一个基于B/S架构的航班信息系统,系统的功能主要就是对航班信息及乘客信息的增删查改,航班信息数据库基于保留格式算法加密存储,所要做的就是在查询航班信息时将其解密显示,以及增删信息时进行加密解密。使用Java编写,系统设计基于SSM(Spring+Spring-MVC+Mybatis)框架。

简要介绍

Spring是一个轻量级的Java开发框架,其使用基本的JavaBean代替EJB,并提供了更多的企业应用功能。其目的在于解决企业应用开发的复杂性。总的来说,Spring是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架。

MVC框架是WEB项目开发的核心环节,将一个复杂的WEB项目分为控制器层(Controller)、视图层(View)、模型层(Model),各个层次分别对应相应的功能。我对MVC架构的理解是,当用户访问一个页面时,首先将响应请求发往控制器层,由控制器处理请求,当请求中有访问数据等操作时,由控制器调用模型层(此时一般会有数据库等操作),返回相应数据,然后回送到视图层,也就是我们所说的jsp页面,并在上面显示。

MyBatis是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

首先是SSM框架的搭建

由于之前已经接触过JavaWeb的开发,但上次完成的系统只是使用普通的servlet+jsp开发,所以这次我打算使用当今流行的Spring-MVC+Spring+Mybatis(SSM)框架。SSM框架的整合对于刚接触Spring的我来说确实是一个不小的挑战。搭建SSM框架的具体流程我在另一篇文章中已经说明,请点这里。项目整体结构如下所示:

具体设计思路

由于本项目的页面部分是基于上次大作业所修改,所以在前端方面并不需要太费功夫。第一步要做的是将数据库操作完成,将generatorConfig.xml的内容配置好后,运行如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
//使用逆向工程,根据数据可中的内容生成代码及sql语句。
public class GenerateSQL {
public static void main(String args[]) throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}

运行完毕后,会发现在事先定义好的dao、mapper、model 包中生成了代码,其中model包中生成了各个表中属性的构造方法,每个表都会生成一个java类,一个表中的所有属性都会生成类中的属性及构造方法。dao包中自动生成了表中的增删查改方法,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
@Repository
public interface CityMapper {
int deleteByPrimaryKey(Integer id);
int insert(City record);
City selectByPrimaryKey(Integer id);
List<City> selectAll();
int updateByPrimaryKey(City record);
City selectByCityName(String cityname);
List<City> selectByProvince(String province);
City selectByDrome(String dromename);
City selectByCityCode(String citycode);
}

不过,自动生成的函数,默认的主码是第一个属性,一般第一个属性都会是主码(例如id),但有时候并不要考主码来查找,例如要根据城市名查找城市三字码的过程中,要有一个根据城市名查找数据库的过程,这时就要自己重写方法。

mapper中包括关于dao包中对应的sql语句,也就是说,当调用dao包中的函数时,函数实际上会执行mapper包中相应的语句,并返回数据库中的数据。mapper中的格式如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dreaouth.dao.CityMapper">
<resultMap id="BaseResultMap" type="com.dreaouth.model.City">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="province" jdbcType="VARCHAR" property="province"/>
<result column="cityname" jdbcType="VARCHAR" property="cityname"/>
<result column="cityCode" jdbcType="CHAR" property="citycode"/>
<result column="dromename" jdbcType="VARCHAR" property="dromename"/>
</resultMap>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from city
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.dreaouth.model.City">
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
insert into city (province, cityname, cityCode,
dromename)
values (#{province,jdbcType=VARCHAR}, #{cityname,jdbcType=VARCHAR}, #{citycode,jdbcType=CHAR},
#{dromename,jdbcType=VARCHAR})
</insert>
<update id="updateByPrimaryKey" parameterType="com.dreaouth.model.City">
update city
set province = #{province,jdbcType=VARCHAR},
cityname = #{cityname,jdbcType=VARCHAR},
cityCode = #{citycode,jdbcType=CHAR},
dromename = #{dromename,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
</update>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select id, province, cityname, cityCode, dromename
from city
where id = #{id,jdbcType=INTEGER}
</select>
<select id="selectByCityName" parameterType="java.lang.String" resultMap="BaseResultMap">
select id, province, cityname, cityCode, dromename
from city
where cityname = #{cityname,jdbcType=VARCHAR}
</select>
<select id="selectByCityCode" parameterType="java.lang.String" resultMap="BaseResultMap">
select id, province, cityname, cityCode, dromename
from city
where cityCode = #{citycode,jdbcType=VARCHAR}
</select>
<select id="selectByProvince" parameterType="java.lang.String" resultMap="BaseResultMap">
select id, province, cityname, cityCode, dromename
from city
where province = #{province,jdbcType=VARCHAR}
</select>
<select id="selectByDrome" parameterType="java.lang.String" resultMap="BaseResultMap">
select id, province, cityname, cityCode, dromename
from city
where dromename = #{dromename,jdbcType=VARCHAR}
</select>
<select id="selectAll" resultMap="BaseResultMap">
select id, province, cityname, cityCode, dromename
from city
</select>
</mapper>

以上就是服务器与数据库传送数据的主要过程,可以根据自己不同的需求去更改和完善。接下来就是最重要的逻辑层。逻辑层无非就是处理jsp页面的不同请求然后返回不同结果。这里我将逻辑层分为两层结构,分别为controller与service,controller层中主要负责接收服务器请求并返回到相应页面以及数据传输,也就是说,controller层主要用于控制请求的流向,而当涉及到具体的功能,例如加密解密数据或检查数据是否有错误等操作时,在service层进行处理。对于SSM框架来说,controller层一般使用注解的方式声明事务,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@Controller
public class LoginController {
@Autowired
private LoginService loginService;
public static User user;
@RequestMapping("/login")
public String login(HttpServletRequest request) {
request.setAttribute("registerOK", "no");
return "login";
}
@RequestMapping("/register")
public String register(HttpServletRequest request) {
request.setAttribute("username", "admin");
return "register";
}
@RequestMapping("/check_login")
public String check_login(HttpServletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = new User(username, password);
int status = loginService.checktUsers(user);
if (status == -2) {
request.setAttribute("check", "-2");
request.setAttribute("registerOK", "no");
return "login";
} else if (status == -1) {
request.setAttribute("check", "-1");
request.setAttribute("registerOK", "no");
return "login";
} else {
user = loginService.getUser(user.getName());
this.user = user;
return "redirect:welcome";
}
}
@RequestMapping("/check_register")
public String check_register(HttpServletRequest request) {
String username = request.getParameter("username");
String e_mail = request.getParameter("e_mail");
String password = request.getParameter("password");
String passowrd2 = request.getParameter("password2");
if (!passowrd2.equals(password)) {
request.setAttribute("check", "3");
return "register";
}
Pattern pattern = Pattern.compile("[a-zA-Z0-9_]*@[0-9A-Za-z.]+$");
Matcher matcher = pattern.matcher(e_mail);
if (!matcher.matches()) {
request.setAttribute("check", "2");
return "register";
}
User user = new User(username, password, e_mail, "user");
if (loginService.register(user)) {
request.setAttribute("registerOK", "ok");
return "login";
} else {
request.setAttribute("check", "1");
return "register";
}
}
}

可以看出,通过注解的方式,例如@RequestMapping("/login"),可以自动响应jsp页面的相应请求,使用request返回数据,并直接return到相应jsp页面。request.setAttribute("", "");方法可以通过键值对的方式返回数据到jsp页面,然后在jsp页面中使用java语句接收并显示出来,这也是jsp相较html更方便的操作了。service层就是一些简单的判断管理员操作异常的逻辑功能以及加密解密航班数据的操作,没什么可说的。

总结

其实总的来说,整个航班信息系统的设计过程并不算很难,但是由于第一次接触web项目,所以还是走了很多坑,个人认为,做一个web工程要提前构思整个项目的流程,然后小组内每个成员分工来完成,不要几个人一起完成一个部分。完成一个web工程可以学到很多东西,感觉最麻烦的应该是前端部分吧,前端对我来说就是很杂碎的东西,各个方面都要自己了解一点,也花费了我最多时间。

感觉自己还是仅仅掌握了Spring框架的皮毛,只学会了使用Spring框架的事务处理,但还是没掌握精髓,记得当时自己看视频学习时感觉一脸懵逼,而且丝毫没有意识到Spring框架方便在哪里,但是当自己实际动手做时才慢慢了解。虽然以后不打算做这方面的东西,不过多了解一些也好,以后有机会再学。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2016 - 2019 Dreaouth All Rights Reserved.

访客数 : | 访问量 :