【发布时间】:2023-01-30 22:04:52
【问题描述】:
我有一个帮助类 AES 加密/解密一个字符串,它主要是 Baeldung AES encryption example 中提供的代码的克隆
代码如下所示:
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class CryptoHelper {
private static final String CIPHER_ALGORITM_NAME = "AES/CBC/PKCS5Padding";
private static final String HASHING_ALGO_NAME = "PBKDF2WithHmacSHA1";
private static final int KEY_TARGET_LENGTH = 256;
private static final int HASHING_ITERATIONS = 65536;
public static SecretKey getSecretKey(String string, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
KeySpec spec = new PBEKeySpec(string.toCharArray(), salt, HASHING_ITERATIONS, KEY_TARGET_LENGTH);
try {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(HASHING_ALGO_NAME);
SecretKey encryptedPassword = new SecretKeySpec(keyFactory.generateSecret(spec).getEncoded(), "AES");
return encryptedPassword;
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw e;
}
}
public static IvParameterSpec getInitializationVector() {
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
return new IvParameterSpec(iv);
}
public static String encrypt(String input, String password, byte[] salt)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITM_NAME);
SecretKey secretKey = null;
try {
secretKey = getSecretKey(password, salt);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
// code
}
cipher.init(Cipher.ENCRYPT_MODE, secretKey, getInitializationVector());
byte[] cipherText = cipher.doFinal(input.getBytes());
return Base64.getEncoder().encodeToString(cipherText);
}
public static String decrypt(String encryptedText, String password, byte[] salt)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITM_NAME);
SecretKey secretKey = null;
try {
secretKey = getSecretKey(password, salt);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
// code
}
cipher.init(Cipher.DECRYPT_MODE, secretKey, getInitializationVector());
byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
return new String(plainText);
}
}
现在我用一个单元测试来测试这个,但是这个测试失败了
javax.crypto.BadPaddingException:给定的最终块不正确 填充。如果在解密过程中使用了错误的密钥,就会出现此类问题。
@Test public void testDecrypt() { String encryptedString = ""; String password = "password"; try { encryptedString = CryptoHelper.encrypt("some string", password, password.getBytes()); } catch (InvalidKeyException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | BadPaddingException | IllegalBlockSizeException e) { e.printStackTrace(); assertNull(e); } try { CryptoHelper.decrypt(encryptedString, password, password.getBytes()); } catch (InvalidKeyException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | BadPaddingException | IllegalBlockSizeException e) { // TODO Auto-generated catch block e.printStackTrace(); } }这里有什么问题?
【问题讨论】:
-
解密失败,因为使用了与加密不同的 IV(因为
getInitializationVector()生成随机 IV)。正确的做法是将加密时使用的IV连同密文一起传递给解密方(注意IV不是秘密的)。