我想要一个安全的解决方案,用于在会话期间在PC上缓存用户密码。
我已经搜索了大量的AES示例,并知道这已经在其他地方得到了回答,但我必须说它有点令人困惑。我的 aesSecretKey 或 aesInitialisationVector 无法正确解密,但不确定问题所在。
解密导致 javax.crypto.BadPaddingException:给定最终块未正确填充异常。
我的班级看起来像这样
public class LockManagerTest {
// Need to share the IV and key between encode and decode
private static byte[] aesInitialisationVector;
private static SecretKey aesSecretKey;
private static Cipher aesCipher;
public LockManagerTest(String sessionKey) {
try {
byte[] key = getSecretKey(sessionKey.toCharArray(), getSalt(32),
65536, 128);
aesSecretKey = new SecretKeySpec(key, "AES");
aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
AlgorithmParameters params = aesCipher.getParameters();
aesInitialisationVector =
params.getParameterSpec(IvParameterSpec.class).getIV();
} catch (Exception e) {
Util.handleException(e);
}
}
private static byte[] getSecretKey(char[] plaintext,
byte[] salt,
int iterations,
int keySize)
throws Exception {
PBEKeySpec spec = new PBEKeySpec(plaintext, salt, iterations, keySize);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return skf.generateSecret(spec).getEncoded();
}
private static byte[] getSalt(int keyLength) throws Exception {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
byte[] salt = new byte[keyLength];
random.nextBytes(salt);
return salt;
}
public byte[] encryptedAes(char[] input) throws Exception {
// WRONG
// aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
//
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey,
new IvParameterSpec(aesInitialisationVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
public byte[] decryptAes(byte[] ciphertext) throws Exception {
aesCipher.init(Cipher.DECRYPT_MODE,
aesSecretKey, new IvParameterSpec(aesInitialisationVector));
byte[] plaintext = aesCipher.doFinal(ciphertext);
return plaintext;
}
}
关于安全程度的评论也表示赞赏。
答案 0 :(得分:1)
在init()
中调用encryptedAes()
时,您需要传递IV。
答案 1 :(得分:0)
AES是一种CBC算法,将输入分为块。这些块必须具有特定的大小。在AES的情况下,我相信它是16个字节。如果输入不是16字节的倍数,则必须在加密前用空值填充。
答案 2 :(得分:0)
您需要传递用于加密的相同IV,而不是在解密时生成新的IV。记住AES是Symmetric Cipher。
编辑: 你在做什么:
public byte[] encryptedAes(char[] input) throws Exception {
// WRONG
// aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
//
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey,
new IvParameterSpec(aesInitialisationVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
而是将IvParameterSpec存储为静态,如下所示(你可以在你的程序中做适当的变量声明)
public byte[] encryptedAes(char[] input) throws Exception {
//declare as static so initVector can be reused when decrypting
IvParamterSpec initVector = new IvParameterSpec(aesSecretKey);
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey, initVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
进行更改,然后运行您的程序。确保在解密时使用相同的initVector。在您的程序中,您正在创建new IvParameterSpec(...)