项目需求:
对一段动态字符串使用openssl加密,并且将加密后的数据发送给另外一个系统。
问题:
看了一下openssl enc命令的帮助信息,发现他的【in】参数只能接收一个文件,对这个文件加密后,输出一个新的加密后的文件。并不接收字符串作为加密对象。
客户给出的方案是,在磁盘上建立一个文件,将动态的字符串写入文件中,然后调用openssl enc加密,再读取加密后的文件,将加密后的数据发送给另外一个系统。完成这个操作后,建立的文件以及openssl输出的文件就没有用了,还需要将这些垃圾文件删除。
虽可行,但是涉及到并发访问以及文件操作,会很麻烦。
解决方案:
几经辗转,终于被我找到一个雅一点的解决方案,不会涉及到并发访问,也没有文件操作。
概要:
这里首先对 openssl enc 的参数【in】和【out】做一下深入的了解。
【in】参数接收一个文件作为加密对象,其实他还可以从标准输入读取数据。而且缺省就是从标准输入读取数据。
【out】参数会输出一个加密后的文件,他也一样可以将加密数据输出到标准输出设备上,而且缺省就是标准输出。
注意:从标准输入读取数据,并非就是在【in】参数后面直接输入加密的字符串,有兴趣的童鞋可以试一下,如果在【in】参数后面直接输入字符串,系统会毫不犹豫的告诉你:“加密的字符串“: No such file or directory
要实现从标准输入读取数据,需要使用管道命令 “|”。稍后会讲解。
OK,数据输入问题解决了。
因为不想涉及文件操作,所以我会将加密后的数据输出到标准输出上,然后从Java程序中捕获输出流,并读取加密数据。
下面就详细讲解实现过程
实现:
先对管道命令做个简单的介绍。基本格式是:command1| command2
简而言之,就是将命令【command1】的输出作为命令【command2】的输入。前提是【command2】必须可以接收标准输入。
第一步:Openssl命令的实现
echo -E "{0}" | openssl aes-128-cbc -e -kfile {1} -base64
1. {0}就是要加密的数据,因为是动态的,我会在命令执行前,将它替换为真实的数据。
2.命令【ehco】就是向标准输出设备输出引号中的内容。这里将使用管道命令”|“将【echo】命令的输出作为【openssl】命令的输入。
注意:参数【-E】的作用是将引号的内容原本输出,而不做转义处理。否则,如果加密数据中含有特殊字符,会导致命令执行失败。
3.命令中没有【out】参数,命令的执行结果会缺省的输出到标准输出上。
4.参数【-kfile】是加密口令,它也必须接收一个文件参数。但是openssl也提供了非文件口令的参数 :【-k ”加密口令“】。加密口令是固定的,所以这里使用【-kefile】的方式,也便于将来管理员修改加密口令。{1}也会在执行命令前,被替换为真实的口令文件名。这不是本文的重点,有所了解即可。
第二部:在Java程序中调用Shell命令
/**
* 数据加密处理
*
* @param data 要加密的数据
* @param commonKey 加密口令文件名
* @return 加密数据
*/
public static final synchronized String encryption(String data, String commonKey){
// 加密后的数据定义
String encryptionData = "";
try {
// 加密命令
String encryption = "echo -E \"{0}\" | openssl aes-128-cbc -e -kfile {1} -base64";
// 替换命令中占位符
encryption = MessageFormat.format(encryption, data, commonKey);
String[] sh = new String[]{"/bin/sh", "-c", encryption};
// Execute Shell Command
ProcessBuilder pb = new ProcessBuilder(sh);
Process p = pb.start();
encryptionData = getShellOut(p);
} catch (Exception e) {
throw new EncryptionException(e);
}
return encryptionData;
}
第三部:在Java程序中捕获Shell命令的输出流,并读取加密数据
/**
* 读取输出流数据
*
* @param p 进程
* @return 从输出流中读取的数据
* @throws IOException
*/
public static final String getShellOut(Process p) throws IOException{
StringBuilder sb = new StringBuilder();
BufferedInputStream in = null;
BufferedReader br = null;
try {
in = new BufferedInputStream(p.getInputStream());
br = new BufferedReader(new InputStreamReader(in));
String s;
while ((s = br.readLine()) != null) {
// 追加换行符
sb.append(ConstantUtil.LINE_SEPARATOR);
sb.append(s);
}
} catch (IOException e) {
throw e;
} finally {
br.close();
in.close();
}
return sb.toString();
}