AES算法加密解密工具类util之改进之动态AES密钥加密
来源:互联网 发布:软件自动化测试工具 编辑:程序博客网 时间:2024/06/14 00:15
AES算法加密解密工具类util之改进之动态AES密钥加密
对于AES算法,我想很多博友都知晓是干嘛用的,本博文就不详细介绍了。作为一种常用的加密算法,AES加密解密我觉得要点在于其key(密钥),一般项目应用中,aesKey是固定的。本文将基于传统的aes加密解密的写法,介绍一种“基于redis缓存动态aes密钥”的方法。
顾名思义,动态aes密钥,其实就是使得key动态隔一段在变化,而且又不影响原有存在的密码,即在动态自动更换密钥时,需要使用原有的key进行解密再使用新生成的aesKey进行加密,并将新的aesKey进行存储。
以上即为缓存动态密钥进行加密解密的思路。下面首先介绍一下固定aesKey的写法:
import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.crypto.Cipher;import javax.crypto.KeyGenerator;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.security.SecureRandom;import java.util.UUID;/** * Created by debug on 2017/10/15. */public class TestUtil { private static final Logger log= LoggerFactory.getLogger(TestUtil.class); public static void main(String[] args){ /*EnumSet<SourceEnum> set=EnumSet.allOf(SourceEnum.class); for(SourceEnum e:set){ log.debug("enum值: {},{} ",e.toString(),e.getCode()); }*/ String aesKey= "36c82834-3fe4-4305-b6e0-39d52e5113d4"; String password="123456"; String resPass=new String(encryptAES(password,aesKey)); log.debug("aesKey={} 加密的结果: {} ",aesKey,resPass); String srcPass=new String(decryptAES(encryptAES(password,aesKey),aesKey)); log.debug("解密的结果: {} ",srcPass); } /** * AES加密 * * @param content 需要加密的内容 * @param key 加密密钥 * @return */ public static byte[] encryptAES(String content, String key) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128, new SecureRandom(key.getBytes())); SecretKey secretKey = kgen.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec keySpec = new SecretKeySpec(enCodeFormat, "AES"); Cipher cipher = Cipher.getInstance("AES");// 创建密码器 byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, keySpec);// 初始化 byte[] result = cipher.doFinal(byteContent); return result; // 加密 }catch (Exception e) { e.printStackTrace(); } return null; } /** * AES解密 * @param content 待解密内容 * @param key 解密密钥 * @return */ public static byte[] decryptAES(byte[] content, String key) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128, new SecureRandom(key.getBytes())); SecretKey secretKey = kgen.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec keySpec = new SecretKeySpec(enCodeFormat, "AES"); Cipher cipher = Cipher.getInstance("AES");// 创建密码器 cipher.init(Cipher.DECRYPT_MODE, keySpec);// 初始化 byte[] result = cipher.doFinal(content); return result; // 加密 }catch (Exception e) { e.printStackTrace(); } return null; }}
如果,你只是需要一种比较安全,简单的aes加密解密写法,你可以参考上面的代码即可(不过,还是希望使用前 自己改造一番,比如不要硬编码之类的)
下面就结合“用户注册与登录”的应用场景讲解“缓存动态密钥加密解密”:
(1)首先创建 注册的user表以及动态变化的aesKey表
create table tb_user(id int auto_incrementprimary key,username varchar(255) null comment '用户名',password varchar(255) null comment '密码',create_time datetime null comment '创建时间',constraint idx_usernameunique (username));
create table tb_encrypt_key(id int auto_increment comment '主键'primary key,alg_key varchar(255) not null comment '加密key',create_time datetime null comment '创建时间');
(2)逆向生成mapper与model我就不多说了(不懂的话,可以加入后面的群或者看我以往的博客: mybatis逆向 生成model与mapper工具的博文)
(3)开发UserService与UserController组件
package com.debug.springboot.service;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.env.Environment;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.scheduling.annotation.EnableScheduling;import org.springframework.stereotype.Service;import javax.crypto.Cipher;import javax.crypto.KeyGenerator;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.security.SecureRandom;import java.util.Base64;/** * v1、对key进行Base64编码 作为key - 只是增加了原始key的复杂度 * v2、对明文密码已经处理完成的二进制串进行过Base64编码解码-可以解码 * Created by debug on 2017/10/21. */@Servicepublic class UserService { //AES 算法 private static final String Encrypt_Alg="AES"; //加密串时选用的字符编码 private static final String Char_Unicode="UTF-8"; //keyGen位数 private static final Integer Key_Size=128; private static final Logger log= LoggerFactory.getLogger(UserService.class); /** * 加密密码 * @param passwordStr * @return */ public String encryptPassword(String passwordStr,String key){ byte[] encryptBytes=encrypt(passwordStr,key); String encryptStr=parseByte2HexStr(encryptBytes); return encryptStr; } /** * 解密密码 * @param passwordHex * @return */ public String decryptPassword(String passwordHex,String key){ byte[] decryptBytes=decrypt(parseHexStr2Byte(passwordHex),key); String decryptStr=new String(decryptBytes); return decryptStr; } /** * 加密 * @param content * @return */ private byte[] encrypt(String content,String aesKey) { try{ KeyGenerator kgen = KeyGenerator.getInstance(Encrypt_Alg); kgen.init(Key_Size,new SecureRandom(Base64.getEncoder().encode(aesKey.getBytes()))); //v3 kgen.init(Key_Size,new SecureRandom(aesKey.getBytes())); SecretKey secretKey = kgen.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec key = new SecretKeySpec(enCodeFormat,Encrypt_Alg); /**创建密码器**/ Cipher cipher = Cipher.getInstance(Encrypt_Alg); byte[] byteContent = content.getBytes(Char_Unicode); /**初始化密码器**/ cipher.init(Cipher.ENCRYPT_MODE, key); byte[] result = cipher.doFinal(byteContent); //v1 //byte[] result = Base64.getEncoder().encode(cipher.doFinal(byteContent)); //v2 return result; }catch(Exception e) { log.error("加密发生异常: {} ",content,e.fillInStackTrace()); } return null; } /** * 解密 * @param content * @return */ private byte[] decrypt(byte[] content,String aesKey) { try{ KeyGenerator kgen = KeyGenerator.getInstance(Encrypt_Alg); kgen.init(Key_Size,new SecureRandom(Base64.getEncoder().encode(aesKey.getBytes()))); //--解密不了 v3 kgen.init(Key_Size,new SecureRandom(aesKey.getBytes())); SecretKey secretKey = kgen.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec key = new SecretKeySpec(enCodeFormat,Encrypt_Alg); /**创建密码器**/ Cipher cipher = Cipher.getInstance(Encrypt_Alg); /**初始化密码器**/ cipher.init(Cipher.DECRYPT_MODE, key); byte[] result = cipher.doFinal(content); //v1 //byte[] result=cipher.doFinal(Base64.getDecoder().decode(content)); //v2 return result; }catch(Exception e) { log.debug("解密过程发生异常: {} ",content,e.fillInStackTrace()); } return null; } /** * 将二进制转换成十六进制 * @param buf * @return */ private String parseByte2HexStr(byte buf[]) { StringBuffer sb = new StringBuffer(); for(int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if(hex.length() == 1) { hex = '0'+ hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } /** * 将十六进制转换为二进制 * @param hexStr * @return */ private byte[] parseHexStr2Byte(String hexStr) { if(hexStr.length() < 1) { return null ; }else{ byte[] result =new byte[hexStr.length() / 2]; for(int i = 0; i < hexStr.length() / 2; i++) { int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16); int low = Integer.parseInt(hexStr.substring(i * 2 + 1,i * 2 + 2),16); result[i] = (byte) (high * 16 + low); } return result; } }}
上面我还尝试了两种写法,就是加上Base64编码解码增加复杂度而已。
package com.debug.springboot.controller;import com.debug.springboot.mapper.TbEncryptKeyMapper;import com.debug.springboot.mapper.TbUserMapper;import com.debug.springboot.model.TbEncryptKey;import com.debug.springboot.model.TbUser;import com.debug.springboot.response.BaseResponse;import com.debug.springboot.response.Status;//import com.debug.springboot.utils.AESUtil;import com.debug.springboot.service.UserService;import com.google.common.base.Strings;import com.google.common.collect.Maps;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.env.Environment;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.scheduling.annotation.EnableScheduling;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.transaction.annotation.Transactional;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import javax.annotation.PostConstruct;import java.util.Date;import java.util.List;import java.util.Map;import java.util.UUID;import java.util.concurrent.TimeUnit;/** * Created by debug on 2017/10/20. */@RestController@EnableSchedulingpublic class UserController { private static final Logger log= LoggerFactory.getLogger(UserController.class); private static final String prex="user"; @Autowired private UserService userService; @Autowired private TbEncryptKeyMapper keyMapper; @Autowired private TbEncryptKeyMapper encryptKeyMapper; @Autowired private TbUserMapper userMapper; @Autowired private Environment env; @Autowired private StringRedisTemplate redisTemplate; //AES加密需要的key //@Value("${encrypt.aes.key}") private String aesKey; @PostConstruct public void init() throws Exception{ aesKey=keyMapper.selectNewestKey(); log.debug("当前的key: {} ",aesKey); }
//此即为动态更换aes密钥并进行缓存的核心代码 @Scheduled(cron = "0 0/45 * * * ?") @Transactional(rollbackFor = Exception.class) public void scheduledUpdateKey() throws Exception{ if (!redisTemplate.hasKey(env.getProperty("redis.key"))){ String newKey= UUID.randomUUID().toString(); redisTemplate.opsForValue().set(env.getProperty("redis.key"),newKey,env.getProperty("redis.key.timeout",Long.class), TimeUnit.MINUTES); TbEncryptKey keyEntity=new TbEncryptKey(); keyEntity.setCreateTime(new Date()); keyEntity.setAlgKey(newKey); List<TbUser> userAll=userMapper.selectAll(); for(TbUser u:userAll){ log.debug("当前用户:{} key:{} 密码: {} ",u.getUsername(),aesKey,u.getPassword()); String uPass=userService.decryptPassword(u.getPassword(),aesKey); log.debug("解密: {} ",uPass); u.setPassword(userService.encryptPassword(uPass,newKey)); userMapper.updateByPrimaryKeySelective(u); } log.debug("oldKey={} 已过期, newKey={} ",aesKey,newKey); aesKey=newKey; encryptKeyMapper.insertSelective(keyEntity); log.debug("现在真正的key: {} ",aesKey); } } /** * 用户注册 * @param userName * @param password * @return * @throws Exception */ @RequestMapping(value = prex+"/register",method = RequestMethod.POST) public BaseResponse register(@RequestParam("userName") String userName,@RequestParam("password") String password) throws Exception{ if (Strings.isNullOrEmpty(userName) || Strings.isNullOrEmpty(password)){ return new BaseResponse(Status.Invalid_Params); } if (userMapper.selectByUserName(userName)!=null){ return new BaseResponse(Status.User_Name_Has_Exist); } BaseResponse response=new BaseResponse(Status.Success); try { TbUser user=new TbUser(); user.setUsername(userName); //user.setPassword(AESUtil.encryptPassword(password)); user.setPassword(userService.encryptPassword(password,this.aesKey)); user.setCreateTime(new Date()); userMapper.insertSelective(user); Map<String,Object> dataMap= Maps.newHashMap(); //dataMap.put("pass",AESUtil.decryptPassword(user.getPassword())); dataMap.put("pass",userService.decryptPassword(user.getPassword(),this.aesKey)); response.setData(dataMap); }catch (Exception e){ log.error("用户注册发生异常:{},{} ",userName,password,e.fillInStackTrace()); return new BaseResponse(Status.Fail); } return response; } /** * 用户登录 * @param userName * @param password * @return * @throws Exception */ @RequestMapping(value = prex+"/login",method = RequestMethod.POST) public BaseResponse login(@RequestParam("userName") String userName,@RequestParam("password") String password) throws Exception{ if (StringUtils.isEmpty(userName) || StringUtils.isEmpty(password)){ return new BaseResponse(Status.Invalid_Params); } TbUser user=userMapper.selectByUserName(userName); if (user==null){ return new BaseResponse(Status.User_Not_Exist); } BaseResponse response=new BaseResponse(Status.Success); try { String passwordHexStr=user.getPassword(); //String passwordSrcHexStr=AESUtil.encryptPassword(password); String passwordSrcHexStr=userService.encryptPassword(password,this.aesKey); log.debug("user的16进制密码串: {} 待比较的16进制密码串:{} ",passwordHexStr,passwordSrcHexStr); if (passwordSrcHexStr.equals(passwordHexStr)){ Map<String,Object> dataMap=Maps.newHashMap(); dataMap.put("userName",user.getUsername()); //dataMap.put("password",AESUtil.decryptPassword(passwordHexStr)); dataMap.put("password",userService.decryptPassword(passwordHexStr,this.aesKey)); response.setData(dataMap); }else{ return new BaseResponse(Status.User_Password_Not_Match); } }catch (Exception e){ log.error("用户登录发生异常:{},{} ",userName,password,e.fillInStackTrace()); return new BaseResponse(Status.Fail); } return response; }} 其中,需要使用到的配置文件内容:
#加密encrypt.aes.key=b6eb4c32-6441-4b63-aae0-6a5cd7a46039encrypt.aes.timeout=1redis.key=aes:encrypt:keyredis.key.timeout=1
(4)最后,当然是模拟用户登录与注册的postman测试,看看效果:其中,注册返回的response的data中pass字段其实经过解密后的,可以从这段代码看出:dataMap.put("pass",userService.decryptPassword(user.getPassword(),this.aesKey));
最后,当然是登录啦:从代码可以看出,登录是对request接收到的password进行加密,然后去数据库对应userName的加密串进行匹配,如果相同,那就说明用户输入的密码是正确的(这也是目前大多数应用中“登录”逻辑的写法)好了,没什么介绍的了,总结一下:其实这种写法,在基于传统的固定key的安全写法上,变得更为安全,代码中隔一段时间更换key的写法我觉得才是核心所在,实际应用中,你可以设置为一个月或者两个月动态更换一次,这个具体可以根据业务来定。最后晒一下我经常变换的aesKey的记录表:就先介绍到这里吧,如果有问题可以加入群讨论: java开源技术交流:583522159 鏖战八方(开源群):391619659
阅读全文
0 0
- AES算法加密解密工具类util之改进之动态AES密钥加密
- 加密解密之AES算法
- AES加密解密工具类
- AES加密、解密工具类
- android之AES加密解密
- Swift-AES之加密解密
- Android之"AES"加密解密
- AES加密/解密算法
- AES加密解密算法
- AES加密解密类
- 【工具类】Java实现AES算法 加密和解密
- 【工具类】AES加密和解密
- 【工具类】AES加密和解密
- Java AES加密解密工具类
- AES加密和解密工具类
- AES 对称加密解密工具类
- Java 加密解密之对称加密算法AES
- Java 加密解密之对称加密算法AES
- SQL Server 触发器
- 常见meta和link标签
- notepadd++和TCC/MinGW构建c/c++环境
- leetcode---reverse-linked-list-ii---链表
- 分享Ctrlbox项目管理软件的财务管理使用心得
- AES算法加密解密工具类util之改进之动态AES密钥加密
- CSS/HTML外边距的叠加问题
- Jni 使用总结第二篇:c程序调用java
- [CentOS7环境搭建](二) NAT模式固定IP设置
- GameEntity(二)—— EntityType
- JAVA设计模式之单例模式
- mysql添加,删除约束,修改各种constraint
- Groovy入门教程
- 软件密钥汇总