使用3Des加密算法前,我们需要了解一下当前主流的加密模式:单向加密和双向加密,两者最大的区别在于加密的密文是否具有可逆性。
单向加密:将需要加密的数据进行加密,并且密文不可进行解密,像我们常用的加密算法MD5就属于这种。
双向加密:和单向加密不同的是可以通过某些方式进行加解密的操作,其中分为对称加密和非对称加密。
对称加密:指数据使用者必须拥有相同的密钥才可以进行加密解密,就像彼此约定的一串暗号,本文介绍的3Des加密就属于这种。
非对称加密:通过一组包含公钥和私钥的密码来加密解密,用公钥加密,私钥解密,首推的就是RSA加密
---------------------------------------------------------------------------------------------------------------------------------------
3Des加密算法,由于可以逆推原文,所以主要通过本地的唯一密钥来保证数据的安全性,我这边通过生成随机的256位加密字符串存储在本地,代码读取时将其通过md5加密成32位的字符串(由于本地有原始密钥,不必担心md5加密不可逆),最后以这32位加密字符串作为密钥进行加解密的操作。
package com.youfuli.util;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import Decoder.BASE64Decoder;
import Decoder.BASE64Encoder;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.util.UUID;
/**
* Created by lixiaotian on 2018/12/25.
*/
public class Des3Util {
private static final String IV = "1234567-";
/**
* DESCBC加密
*
* @param src 数据源
* @param key 密钥,长度必须是8的倍数
* @return 返回加密后的数据
* @throws Exception
*/
public String encryptDESCBC(final String src, final String key) throws Exception {
// --生成key,同时制定是des还是DESede,两者的key长度要求不同
final DESKeySpec desKeySpec = new DESKeySpec(key.getBytes("UTF-8"));
final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
final SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
// --加密向量
final IvParameterSpec iv = new IvParameterSpec(IV.getBytes("UTF-8"));
// --通过Chipher执行加密得到的是一个byte的数组,Cipher.getInstance("DES")就是采用ECB模式,cipher.init(Cipher.ENCRYPT_MODE,
// secretKey)就可以了.
final Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
final byte[] b = cipher.doFinal(src.getBytes("UTF-8"));
// --通过base64,将加密数组转换成字符串
final BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(b);
}
/**
* DESCBC解密
*
* @param src 数据源
* @param key 密钥,长度必须是8的倍数
* @return 返回解密后的原始数据
* @throws Exception
*/
public String decryptDESCBC(final String src, final String key) throws Exception {
// --通过base64,将字符串转成byte数组
final BASE64Decoder decoder = new BASE64Decoder();
final byte[] bytesrc = decoder.decodeBuffer(src);
// --解密的key
final DESKeySpec desKeySpec = new DESKeySpec(key.getBytes("UTF-8"));
final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
final SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
// --向量
final IvParameterSpec iv = new IvParameterSpec(IV.getBytes("UTF-8"));
// --Chipher对象解密Cipher.getInstance("DES")就是采用ECB模式,cipher.init(Cipher.DECRYPT_MODE,
// secretKey)就可以了.
final Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
final byte[] retByte = cipher.doFinal(bytesrc);
return new String(retByte);
}
// 3DESECB加密,key必须是长度大于等于 3*8 = 24 位哈
public static String encryptThreeDESECB(final String src, final String key) throws Exception {
final DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));
final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
final SecretKey securekey = keyFactory.generateSecret(dks);
final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, securekey);
final byte[] b = cipher.doFinal(src.getBytes());
final BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(b).replaceAll("\r", "").replaceAll("\n", "");
}
// 3DESECB解密,key必须是长度大于等于 3*8 = 24 位哈
public static String decryptThreeDESECB(final String src, final String key) throws Exception {
// --通过base64,将字符串转成byte数组
final BASE64Decoder decoder = new BASE64Decoder();
final byte[] bytesrc = decoder.decodeBuffer(src);
// --解密的key
final DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));
final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
final SecretKey securekey = keyFactory.generateSecret(dks);
// --Chipher对象解密
final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, securekey);
final byte[] retByte = cipher.doFinal(bytesrc);
return new String(retByte);
}
}
此处提供的代码中,展现了des加解密和3des加解密的相应的方法。des和3Des加密算法的区别在于des加密算法的实际密文长度为56位,导致加密长度不足,才会出现3Des加密算法来进行弥补的,通过执行3次DES来达到增加密钥长度和安全的目的。
这边我提供一个测试类,用于测试生成本地唯一密钥文件和md5加密本地密钥文件,然后采用3Des对需要加解密的数据进行加解密的操作。
public static void main(String[] args) throws Exception {
String str2 = "";
StringBuilder result = new StringBuilder();
File newfile = new File("D:/decrypt");
if (!newfile.exists()) {
newfile.mkdirs();
File file = new File("D:/decrypt"+"/decrypt.txt");
if(!file.exists()) {
file.createNewFile();
}
for (int i=0;i<8;i++) {
UUID uuid = UUID.randomUUID();
String str = uuid.toString();
String str1 = str.substring(0, 8) + str.substring(9, 13) + str.substring(14, 18) + str.substring(19, 23) + str.substring(24);
str2 = str2+str1;
}
FileOutputStream fileOutputStream = null;
fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(str2.getBytes("UTF-8"));
fileOutputStream.close();
}
BufferedReader bfr = new BufferedReader(new InputStreamReader(new FileInputStream("D:/decrypt/decrypt.txt"), "UTF-8"));
String lineTxt = null;
while ((lineTxt = bfr.readLine()) != null) {
result.append(lineTxt).append("\n");
}
bfr.close();
str2 = result.toString();
System.out.println(str2);
String md5Key = md5.getMD5Str(str2);
System.out.println("密钥:"+md5Key);
String keys = "654321";
Des3Util des3 = new Des3Util();
String txt = des3.encryptThreeDESECB(keys, md5Key);
System.out.println("加密数据:"+txt);
String decy = des3.decryptThreeDESECB(txt,md5Key);
System.out.println("解密数据:"+decy);
}
可以看出,我本地通过uuid循环八次随机生成密钥文件,然后对其进行md5,最后调用相应的方法进行加解密的操作,最后对需要加密的数据“654321”的加解密的操作结果如下:
第一行:打印出本地长度为256位的原始密钥
第二行:打印出使用md5加密后的密钥
第三行:对654321进行加密后的加密数据
第四行:对加密数据preJaIoIzLw=进行解密,最后还原为明文654321
---------------------------------------------------------------------------------------------------------------------------------------
此次采用3Des加密算法,在于把本地配置文件中的密码配置进行加密化操作,将配置文件中的密码加密保存在数据库中,然后再读取时配置在缓存中。
package com.youfuli.util;
import com.youfuli.dao.FlxThirdAccountInfoMapper;
import com.youfuli.entity.FlxThirdAccountInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import java.io.*;
import java.util.*;
/**
* Created by lixiaotian on 2018/12/25.
*/
public class ThirdAccountMap {
private static Logger logger = LoggerFactory.getLogger(ThirdAccountMap.class);
//账户map
private static Map accMap;
static FlxThirdAccountInfoMapper thirdAccountInfoMapper = SpringUtil.getBean(FlxThirdAccountInfoMapper.class);
static {
try {
List thirdAccountInfos = thirdAccountInfoMapper.findAccount();
if (CollectionUtils.isEmpty(thirdAccountInfos)) {
logger.info("数据库还未导入加密密码");
}
Properties prop = PropertiesUtil.getInstance();
String decryptUrl = prop.getProperty("decrypt.url");
StringBuilder result = new StringBuilder();
File newfile = new File(decryptUrl);
String txtFile = decryptUrl + "/decrypt.txt";
File file = new File(txtFile);
//文件不存在的情况
if (!file.exists()) {
if (!newfile.exists()) {
newfile.mkdirs();
}
file.createNewFile();
String str2 = "";
for (int i = 0; i < 8; i++) {
UUID uuid = UUID.randomUUID();
String str = uuid.toString();
String str1 = str.substring(0, 8) + str.substring(9, 13) + str.substring(14, 18) + str.substring(19, 23) + str.substring(24);
str2 = str2 + str1;
}
FileOutputStream fileOutputStream = null;
fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(str2.getBytes("UTF-8"));
fileOutputStream.close();
}
BufferedReader bfr = new BufferedReader(new InputStreamReader(new FileInputStream(new File(txtFile)), "UTF-8"));
String lineTxt = null;
while ((lineTxt = bfr.readLine()) != null) {
result.append(lineTxt).append("\n");
}
bfr.close();
String str = md5.getMD5Str(result.toString());
accMap = new HashMap<>();
for (int i = 0; i < thirdAccountInfos.size(); i++) {
String keys = thirdAccountInfos.get(i).getAccountKey();
String values = thirdAccountInfos.get(i).getAccountValue();
if ("1".equals(thirdAccountInfos.get(i).getIsEncrypt())) {
accMap.put(keys, Des3Util.decryptThreeDESECB(values, str));
} else {
accMap.put(keys, values);
}
}
logger.info("数据库解密密码完成");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据账户key进行查询
*
* @param accountKey
* @return
*/
public static String queryThirdKey(String accountKey) {
if (accMap == null) {
logger.error("数据库加密信息未加载");
return null;
}
return accMap.get(accountKey);
}
}
通过数据库中的key匹配key即可。
appKey = ThirdAccountMap.queryThirdKey("appKey");
其中还对文件路径下的文件做了处理,如果不存在即进行新建处理,个人觉得还有优化的空间。一起加油哦。