javax.crypto.BadPaddingException
2020-01-09 本文已影响0人
流沙飞雪
在做AES加解密的时候遇到这个问题,发现win现每次加密结果都一样,但是linux下每次都是随机的,后来在网上找到原因。
原因:SecureRandom 实现完全随操作系统本身的內部状态,除非调用方在调用 getInstance 方法,然后调用 setSeed 方法;该实现在 windows 上每次生成的 key 都相同,但是在 solaris 或部分 linux 系统上则不同。关于SecureRandom类的详细介绍,参考网上文章
解决办法:调用getInstance方法
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG") ;
secureRandom.setSeed(encodeRules.getBytes());
最终代码如下:
@Slf4j
public class AESUtil {
/**
* 注意key和加密用到的字符串是不一样的 加密还要指定填充的加密模式和填充模式 AES密钥可以是128或者256,加密模式包括ECB, CBC等 * ECB模式是分组的模式,CBC是分块加密后,每块与前一块的加密结果异或后再加密 第一块加密的明文是与IV变量进行异或
*/
public static final String KEY_ALGORITHM = "AES";
public static final String ECB_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
public static final String RANDOM_ALGORITHM = "SHA1PRNG";
public static final String ENCODING = "UTF-8";
/*
* 加密
* 1.构造密钥生成器
* 2.根据ecnodeRules规则初始化密钥生成器
* 3.产生密钥
* 4.创建和初始化密码器
* 5.内容加密
* 6.返回字符串
*/
public static SecretKeySpec genKey(String myKey) {
//MessageDigest sha = null;
try {
//1.构造密钥生成器,指定为AES算法,不区分大小写
KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
//2.根据myKey规则初始化密钥生成器
//生成一个128位的随机源,根据传入的字节数组
SecureRandom secureRandom = SecureRandom.getInstance(RANDOM_ALGORITHM);
secureRandom.setSeed(myKey.getBytes());
kgen.init(128, new SecureRandom(myKey.getBytes()));
//3.产生原始对称密钥
SecretKey secretKey = kgen.generateKey();
//4.获得原始对称密钥的字节数组
byte[] encodeFormat = secretKey.getEncoded();
//5.根据字节数组生成AES密钥
return new SecretKeySpec(encodeFormat, KEY_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
log.error("初始化密钥错误:", e);
}
return null;
}
/**
* 使用ECB模式进行加密。 加密过程三步走: 1. 传入算法,实例化一个加解密器 2. 传入加密模式和密钥,初始化一个加密器 3. * 调用doFinal方法加密
*
* @param strToEncrypt
* @param secret
* @return
*/
public static String encrypt(String strToEncrypt, String secret) {
try {
//6.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance(ECB_CIPHER_ALGORITHM);
//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, genKey(secret));
//8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
//9.根据密码器的初始化方式--加密:将数据加密 cipher.doFinal
//10.将加密后的数据转换为字符串
//这里用Base64Encoder中会找不到包
//解决办法:
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes(ENCODING)));
} catch (Exception e) {
log.error("AES加密错误: ", e);
}
return null;
}
/**
* 解密
*
* @param strToDecrypt
* @param secret
* @return
*/
public static String decrypt(String strToDecrypt, String secret) {
try {
Cipher cipher = Cipher.getInstance(ECB_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, genKey(secret));
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt.getBytes(ENCODING))));
} catch (Exception e) {
log.error("AES解密错误: ", e);
}
return null;
}
public static void main(String[] args) {
final String secretKey = "cZmmOQM#j2X32OaR";
String originalString = "44,2020-01-09";
String encryptedString = AESUtil.encrypt(originalString, secretKey);
String decryptedString = AESUtil.decrypt(encryptedString, secretKey);
System.out.println(originalString);
System.out.println(encryptedString);
System.out.println(decryptedString);
}
}