使用AES缓存用户密码 - BadPaddingException

时间:2013-08-30 02:32:19

标签: java encryption aes

我想要一个安全的解决方案,用于在会话期间在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;
    }
}

关于安全程度的评论也表示赞赏。

3 个答案:

答案 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(...)