/**
* 根据密钥生成一个用于aes的key。
*/
public static SecretKey getSecretKey(int bits, String secret) {
KeyGenerator keyGenerator = null;
try {
keyGenerator = KeyGenerator.getInstance("AES");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("aes[" + bits + "] not supported", e);
}
keyGenerator.init(bits, new SecureRandom(secret.getBytes()));
return keyGenerator.generateKey();
}
/**
* 对文本进行加密。
*
* @param text 将被加密的文本
* @param secretKey 用于加密的key
* @return 已被加密的文本
*/
public static String encrypt(String text, SecretKey secretKey) {
ByteSource byteSource = aesCipherService.encrypt(text.getBytes(), secretKey.getEncoded());
return byteSource.toHex();
}
/**
* 对被加密的文本进行解密。
*
* @param encryptedText 已被加密的文本
* @param secretKey 用于解密的key
* @return 解密后的内容
*/
public static String decrypt(String encryptedText, SecretKey secretKey) {
ByteSource byteSource = aesCipherService.decrypt(Hex.decode(encryptedText), secretKey.getEncoded());
return new String(byteSource.getBytes());
}
在本地机器上运行了平台项目和登入服项目。
平台项目中返回的凭据,在登入服上进行解密会失败。
之前以为是shiro版本问题,平台用的是1.4.0,登入服用的是1.5.3,后来改成相同版本,发现不是这个问题。
为了排查问题,又启动了一个专门写java测试代码的项目,将加密和解密代码放到里面运行,结果出现了一个更诡异的事情。
在测试项目中得到的加密凭据,可以在登入服解密,但是在平台上解密会失败。
经过调试,发现平台生成的key字节数组(secretKey.getEncoded())和其它项目的不一样。
通过对比这几个项目的配置,发现jdk版本不一样:平台用的是jdk1.8.0_251,登入服和测试项目用的是jdk11.0.7。
通过在不同版本的jdk运行这段代码,得到了不同的结果。
byte[] seed = "3X0m2+Kp@zj@ORr*xttVk7~ce&aT22.m^z#j,Zzj".getBytes();
for (int i = 0; i < 10; ++i) {
SecretKey secretKey = AesUtils.getSecretKey(128, "3X0m2+Kp@zj@ORr*xttVk7~ce&aT22.m^z#j,Zzj");
System.out.println(Arrays.toString(secretKey.getEncoded()));
}
jdk1.8.0_251
[32, 58, -78, 112, 122, 104, -86, -51, -77, 110, 122, -33, -123, 124, -13, -20]
[32, 58, -78, 112, 122, 104, -86, -51, -77, 110, 122, -33, -123, 124, -13, -20]
[32, 58, -78, 112, 122, 104, -86, -51, -77, 110, 122, -33, -123, 124, -13, -20]
[32, 58, -78, 112, 122, 104, -86, -51, -77, 110, 122, -33, -123, 124, -13, -20]
jdk11.0.7
[93, -22, 110, 6, -100, 109, -9, -6, -65, 5, 30, 36, 55, -92, -27, -102]
[102, 52, -27, -55, 127, 96, -110, -72, 58, 37, -106, -98, -23, 86, 27, 9]
[-48, -18, -91, -34, -116, 15, -61, 57, -56, 104, 116, 118, -63, -106, 26, 75]
[-21, 97, 49, 126, 123, 103, 104, 40, -51, 99, 98, 8, 84, 41, -14, 54]
可以看到,在jdk8中,只要种子一样,SecureRandom每次生成的字节数组都一样;在jdk11中,每次生成的字节数组不一样。
aes的字节数组key,提前生成就可以了,可以不用SecureRandom方式在运行时生成。
jdk版本引起的SecureRandom问题违反了直觉。