您的位置:首页 > 移动开发

JAVA APP——密码加密后端存储

2013-04-21 22:42 330 查看
一个简单的应用,从python回到JAVA,重新熟悉下JAVA,另外方面前段时间看完设计模式不用上的感觉很快就忘记了,想从设计及开发上多应用下。

一、需求

 平常注册的网站多了,或者小号或者账号等多了,主要怕一些网站明文传输的,很多密码都是临时的那种,但是后面要用又想不起来,
故想做这么个简单应用本地将密码做RSA加密,然后根据网站域名和账号保存到远端存储上。
本来是考虑用有道云笔记的,后来申请权限花了很多时间,API也相对较复杂,就先采用File,FTP,百度云储存三种方式做后端存储。

二、设计

虽然这个需求非常简单,但是从最初考虑好需求,做了一些包括结构层次和类层次的设计,到最后实现,还是发现有很多问题考虑不到,
到后面又得回头修改设计。还有就是当遇到问题的时候没有第一时间记下来,导致现在有些可能都不太记得了,这个以后得注意了。

这个功能想起来会非常清晰,分成三个模块即可,一个是加密解密模块,一个是后端存储服务模块,一个是前端展现模块。
加密解密模块和存储服务互不相干,都由前端调用接口即可,那么考虑好接口将是很重要的一环。

这里考虑到加密解密模块今后变动性不大,基本固定的,风险最小,就先完成这部分的设计和实现。

1.加密解密模块

这里考虑到的问题主要是两个,一个是前端如果要用,可能会用到哪些功能,会需要什么样的方法,什么样的参数,
另外一个是底层实现如何做到可以很融洽地被spring所接收。

加密解密自然设计到密钥,如果用户希望采用对称加密,需要提供IV和Key的方法签名,而非对称则需要公私钥的签名,
最开始的类图如下所示,由于考虑丢了IV的因素,导致后面参数结构上做了一次调整。

a)密钥对

密钥对方面首先是提供给前端的接口,从当前应用来看,主要是会从外面导入一个某格式的公钥或者私钥,需要返回的
是一个密钥对象。密钥格式最好由前端提供,实在不行可采用责任链形式把所有后端实现串起来也可行,这个问题相对
不重要,不放在参数考虑。然后这个密钥从最后实现来看肯定需要一个字节数组的,所以首先考虑一个文件,其次文件
路径,然后如果是一个字节流或者字节数组会更方便。所以这里就提供三种不同的导入接口,通过适配的方式传递给后
端实现。

有个问题是后面才发现,就是取名问题,这也是一直困扰我的问题,因为英语不好?。。貌似是的。总之这里方法名取
的有些问题,后面发现时再去改会很蛋疼。

这里后端实现,最先考虑到的是抽象工厂,基本每一个导入密钥对都是生成同一系列的;第二点想到的是策略,多种实
现可相互替换,这里虽谈不上相互替换,需要指定特定格式才可以,但是后来发现桥接模式非常适用该场景,将复杂的
后端实现全部丢到一边,将接口提供给类适配,可以使得spring IOC的使用上相对简便点。
不过后面想想,即使用对象适配,然后不适用桥接似乎也不会有多复杂,这里好像确实差不多,因为今后的改动也不会
大,也比较不出什么优势,先放着以后有场景再考虑。



b)加密解密

之后是加密解密的接口问题,同样考虑前端可能会传什么的问题,一般来说加密解密都是以字节数组传递,但是前端很多并不关心,可能更需要的是一个
容易处理的Base64码,也有可能就是一个明文字符串,也有可能就传一个字节数组,所以按照前端更简便的方式来提供接口。
但是这些很多参数并不是后端实现所关心的,同样采用一个适配器做一层转换操作。
最终底层实现相对比较单一,一种是非对称,一种是对称,提供相应实现即可。
但是由于最初考虑掉了IV的问题,导致后面硬生生的加了一个IV的get方法以及构造函数,显得有些别扭。
现在考虑的话,可能封装一层Key更加合适,将本身的密钥对Key对象及IV等打包作为Key传入或者在内部改变IV,外面从传入的封装对象获取IV。



2.后端存储问题

后端存储感觉设计上没有什么要说的,主要是考虑到多字段作为key,以及value中的字节流是否放内存读取返回的问题等。
这里就简单的提供策略模式的实现部分,多种可配置后端存储,如果需要多后端同时存储,也可以新增一个实现类MixedStore之类的,
来转发请求到多个后端,下载随机取一个后端下载。
关于后端存储要说的可能更多的在实现部分,这里暂时略过。



3.前端问题

这个应用不同于其他应用,需要在本地加密密码后才能在网络中传输的,也就是说像WEB单一提交表单是不可行的,
当然如要用WEB,可结合安全控件或者java applet等来实现,这里个人需求方面更多是本地客户端,即使是一个简单
的Console终端命令也挺好,有swing更佳。不过后来偷懒了。。。只实现了Console。

三、实现

enterprise architect可提供UML转为代码,设计好的图转换下很方便,结构不需要重新搭建一遍。

1.加密解密模块

关于密钥对的导入实现,前面有过一次讨论了,请移步JAVA解析各种编码密钥对(DER、PEM、openssh公钥) 
因为这三种是最常见的RSA密钥对,其他的格式(包括openssh格式私钥,貌似和PEM格式一样?)暂时没有时间去细看。

a)JCE的块加密

这里提供了AES对称的方式和RSA非对称的方式,有用户不想使用私钥的,可用AES替代,(不过偷懒前端没实现AES替代方式)
实现的时候没怎么注意的问题是,AES的本身就支持超过一个块大小自动分块加密的,而RSA不可以,不过这个影响不大,一测
出来只需改掉实现部分即可。后面实现的时候又抽象出一个BlockEncryption,将AES也抽出来由应用来控制块加密。
这个单元测试也没测试出来,用例提供过于简单导致。

b)块加密的padding

关注块加密的同时,还需要注意到padding的方式,这个会影响到能加密的内容大小的,比如PKCS1Padding需要留11字节保留信息的
不过不知道是否跟密钥对长度相关,具体JAVA支持的参见security guides中的Cipher Algorithm Padding
这个部分个人了解也略少,看网上有说对称加密支持PKCS5Padding(8字节Block)和PKCS7Padding(1到255字节不固定Block)
非对称则支持PKCS1Padding 和 OAEP。

c)加密模式与IV向量

然后还有个需要注意的是加密算法中的加密算法模式,见security guides中的Cipher Algorithm Modes
CBC是需要IV向量的,JCE中如果使用CBC,你又不传IV,JCE会帮你生成一个IV,你需要在后面获取到这个IV用于解密。
如果这个地方不注意,可能你发现你始终解密不出来,ECB则不需要IV即可。

d)bouncycastle和maven打包问题

如果你在应用中需要将依赖打包到一个jar中,比如可执行jar文件,你会发现加载BC时出现
 java.lang.SecurityException: JCE cannot authenticate the provider BC
这个主要是打包之后签名丢掉了,再使用这个会被认证失败。
目前我的解决方案是将原生的bouncycastle包丢到ext中使用扩展类加载器加载即可。

2.后端存储模块

后端存储提供了列举所有key的方法,便于今后查找,
get方法,通过key获取值,在该应用中得到值再使用私钥解密一遍。
save方法,通过key和加密好的值存储到远端保存。
设计方案是以一个文件为单位保存,名字为所有key通过分隔符串接,文件内容为值,
这样设计便于所有后端的实现。

这里因为觉得提供删除接口可能导致很多信息丢失,但是这个地方没提供接口比较麻烦,后期想实现修改很大。
应该是先提供空实现,不应该完全不提供。

a)easymock单元测试

因为后端存储的实现都依赖于外围的功能,这个可以有效利用easymock来解除这个依赖,
这个东西个人用得也不多,以前在python中老是用stub用惯了,easymock有点类似,但是还是有很大差别。
而且大致看了下easymock源码,用了个状态模式,先记下来,算是状态模式的示例吧,
大致实现是在Record模式下动态代理对象只记录调用状态,而不会实际去调用代理对象的方法,
然后在replay模式下比对Record记录。
以后有机会再细看源码,写篇博客分析下。

b)网络代理相关

这个部分以前基本没接触,这次因为要连接一个内网FTP,必须做代理访问才得以第一次接触。
如何使用官网有较详细说明,Java Networking and Proxies
设置系统属性即可,但是别设置错了,比如你的代理类型是SOCKS,别用成HTTP就行了。
然后FTP的代理不知道是什么意思,我这里使用SOCKS代理连接内网FTP服务器,使用SOCKS属性即可,具体以后有时间再研究。

c)百度云盘API

百度云盘API确实好用,比起有道云笔记的API简单多,虽然现在有了云笔记的SDK,以后有时间再尝试该功能。
百度云盘申请到Key后,需要给用户一个确认授权的链接,之后返回的链接中包含session id之类的东西,通过解析该链接保存
这个session id,有效期一个月,配置到配置文件中,后面再认证再更改即可。
然后百度API有个问题是,偶尔会出现网络流卡在read上,即使没有可读的返回个-1嘛。。。具体出什么问题不清楚,应该是服务器端
长连接之类的吧,读取还没关闭。后来有修改为先获取头信息中的length属性,再读取指定长度后退出读取。

3.前端展示

前端模块如何展示完全独立出来,方便今后增加展示方式。
用到spring IOC管理实现类,体会下先前设计的与spring的衔接问题。

a)命令行终端密码保护

public static String readPassword(String prompt) {
if (System.console() != null)
return String
.valueOf(System.console().readPassword("[%s]", prompt));
Scanner scanner = new Scanner(System.in);
System.out.print(prompt);
return scanner.next();
}


可以使用System.console来实现,但是eclipse的Console不支持,又增加一个不支持的情况下的读取,就不保护了。

b)maven打包可执行Jar

<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>org.csp.client.Console</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>


这里把mainClass改下即可,会将所有依赖包打在一起的,前面提到的bouncycastle签名问题也需要注意一下的。

c)linux下修改jar中的配置文件

安装有gzip后,直接可以用vi打开jar文件的,然后在需要修改的配置文件上回车即可打开该文件,修改完保存即可。
这样做方便于将配置文件打包在jar中,需要修改时避免了重新打包问题。

d)编码问题

以前博客有提到过一次,maven编译的编码需要与工程编码一致,
具体在pom.xml中添加一个属性即可
<properties>
<project.build.sourceEncoding>GBK</project.build.sourceEncoding>
</properties>

e)日志

本项目中都没添加Log日志,等代码写完才发现。。。毛病
日志和注释写得都不多,习惯没养成,这个先前还有多次提醒自己的,希望后面慢慢改掉吧

f)类和变量起名问题

不太会取名,经常来个temp,或者以类名的第一个小写解决。。。
再慢慢体会吧。

项目传到github上了,但是是在一个项目的三个分支上,打了三个tag,有兴趣的可以下下来看看。
下面链接中三个TAG对应是,store/1.0是后端存储模块,edm/1.0是加密解密模块,csp-client/1.0是一个Console前端展示
https://github.com/zhouyuzhy/java/tags

maven eclipse:eclipse跑下,用eclipse导入就行了。
重新回到JAVA领域,多多指教~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: