加密,是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使获得了已加密的信息,但因不知解密的方法,仍然无法了解信息的内容。大体上分为双向加密和单向加密,而双向加密又分为对称加密和非对称加密(有些资料将加密直接分为对称加密和非对称加密)。
双向加密大体意思就是明文加密后形成密文,可以通过算法还原成明文。而单向加密只是对信息进行了摘要计算,不能通过算法生成明文,单向加密从严格意思上说不能算是加密的一种,应该算是摘要算法吧。
最近在工作中接到一个需求要对服务进行过期授权的操作,在硬件方面使用加密狗usb(类似U盘的东西),另一种是通过服务端进行过期校验,不论哪种的方式都不会是绝对的安全。
思路:使用一个依赖jar包的方式提供过期时间验证,该包中设置过期时间以及对其加密/解密验证,集成到外部网关服务中对外部请求进行拦截校验。所以jar包要进行防止反编译而暴露验证方式。
在该章文章中先介绍一下我了解到的加密的几种方式,借荐于
https://blog.youkuaiyun.com/u013651026/article/details/79167360
该文章介绍的AES(全称为“Advanced Encryption Standard”,中文名“高级加密标准”,在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种区块加密标准。) 想了解其他的加密方式可借鉴该文章。
下面就介绍本人使用AES加密方式的过程以及遇到的问题。
创建jar工程
- pom.xml
<dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
-
实现类
代码结构:
-
常量类
public class ServiceExpiredConstant {
/*加密后的过期时间*/
public static final String EXPIRED_TIME_STRING = "6MSktJfBKcKr+QmVQPnRCmcGDOw1DEP53mb2f2xtmX4=";
/*时间格式*/
public static final String SIMPLE_DATE_FORMAT_STRING = "yyyy-MM-dd HH:mm:ss";
public static final String KEY_ALGORITHM_STRING = "AES";
/*算法/模式/补码方式*/
public static final String DEFAULT_CIPHER_ALGORITHM_STRING = "AES/ECB/PKCS5Padding";
/**
* 签名算法
*/
public static final String SIGN_ALGORITHMS = "SHA1PRNG";
/*编码格式*/
public static final String UTF_8_STRING = "utf-8";
public static final String PASS_WORD_STRING = "验证码";
}
- 加密工具类
public class AESUtil {
/**
* AES 加密操作
*
* @param content 待加密内容
* @param password 加密密码
* @return 返回Base64转码后的加密数据
*/
public static String encrypt(String content, String password) {
try {
Cipher cipher = Cipher.getInstance(ServiceExpiredConstant.DEFAULT_CIPHER_ALGORITHM_STRING);// 创建密码器
byte[] byteContent = content.getBytes(ServiceExpiredConstant.UTF_8_STRING);
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));// 初始化为加密模式的密码器
byte[] result = cipher.doFinal(byteContent);// 加密
return Base64.encodeBase64String(result);//通过Base64转码返回
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* AES 解密操作
*
* @param content
* @param password
* @return
*/
public static String decrypt(String content, String password) {
try {
//实例化
Cipher cipher = Cipher.getInstance(ServiceExpiredConstant.DEFAULT_CIPHER_ALGORITHM_STRING);
//使用密钥初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password));
//执行操作
byte[] result = cipher.doFinal(Base64.decodeBase64(content));
return new String(result, ServiceExpiredConstant.UTF_8_STRING);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* 生成加密秘钥
*
* @return
*/
private static SecretKeySpec getSecretKey(final String password) {
//返回生成指定算法密钥生成器的 KeyGenerator 对象
KeyGenerator kg = null;
try {
kg = KeyGenerator.getInstance(ServiceExpiredConstant.KEY_ALGORITHM_STRING);
/*补充*/
SecureRandom random = SecureRandom.getInstance(ServiceExpiredConstant.SIGN_ALGORITHMS);
try {
random.setSeed(password.getBytes(ServiceExpiredConstant.UTF_8_STRING));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//AES 要求密钥长度为 128
// kg.init(128, new SecureRandom(password.getBytes()));
kg.init(128, random);
//生成一个密钥
SecretKey secretKey = kg.generateKey();
return new SecretKeySpec(secretKey.getEncoded(), ServiceExpiredConstant.KEY_ALGORITHM_STRING);// 转换为AES专用密钥
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
return null;
}
- 配置类
public class ServiceExpiredConfig {
public static long getEndTime() {
SimpleDateFormat formatter = new SimpleDateFormat(ServiceExpiredConstant.SIMPLE_DATE_FORMAT_STRING);
ParsePosition pos = new ParsePosition(0);
Date strtodate = formatter.parse(AESUtil.decrypt(ServiceExpiredConstant.EXPIRED_TIME_STRING, ServiceExpiredConstant.PASS_WORD_STRING), pos);
return strtodate.getTime();
}
public static long getNowTime() {
return System.currentTimeMillis();
}
public static String encrypt(String content, String password) {
return AESUtil.encrypt(content,password);
}
public static String decrypt(String content, String password) {
return AESUtil.decrypt(content,password);
}
}
- 暴露外部方法类
public class ServiceExpired {
public static Boolean timeExpired() {
if (ServiceExpiredConfig.getNowTime() > ServiceExpiredConfig.getEndTime()) {
return true;
}
return false;
}
public static String encrypt(String content, String password) {
return ServiceExpiredConfig.encrypt(content,password);
}
public static String decrypt(String content, String password) {
return ServiceExpiredConfig.decrypt(content,password);
}
public static Long getEntTime(){
return ServiceExpiredConfig.getEndTime();
}
}
遇到问题:
在本地Windows系统上完全可以,但是在Linux系统上就会报错,
问题出现在校验码加密的方式上,上述代码已是正确的代码,在Linux上也是ok的。
javax.crypto.BadPaddingException: Given final block not properly padded