Java 尝试使用AES加密和解密字符串时出现IllegalBlockSizeException

Java 尝试使用AES加密和解密字符串时出现IllegalBlockSizeException,java,android,encryption,aes,Java,Android,Encryption,Aes,我有一个硬编码密钥,在将字符串存储在SharedReferences中之前,我想用它对字符串进行加密。这是我目前掌握的代码: public class TokenEncryptor { private final static String TOKEN_KEY = "91a29fa7w46d8x41"; public static String encrypt(String plain) { try { Cipher cipher =

我有一个硬编码密钥,在将字符串存储在
SharedReferences
中之前,我想用它对字符串进行加密。这是我目前掌握的代码:

public class TokenEncryptor {

    private final static String TOKEN_KEY = "91a29fa7w46d8x41";

    public static String encrypt(String plain) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
            SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES");
            cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
            return new String(cipher.doFinal(plain.getBytes()));
        } catch (Exception e) {
            Ln.e(e);
            return null;
        }
    }

    public static String decrypt(String encoded) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
            SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES");
            cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
            return new String(cipher.doFinal(encoded.getBytes()));
        } catch (Exception e) {
            Ln.e(e);
            return null;
        }
    }
}
它似乎在
decrypt
方法的末尾捕获到异常:

javax.crypto.IllegalBlockSizeException:错误:0606506D:数字信封例程:EVP_DecryptFinal_ex:错误的最终块长度


有人能给我指出正确的方向吗?我感觉我在实例化
IvParameterSpec

时出错了当你用AES加密一个字符串时,你会得到一个字节数组。尝试将这些字节直接转换为字符串(
新字符串(cipher.doFinal(plaintextBytes))
)将导致各种问题。如果要求加密方法的输出为字符串,请使用
Base64
,而不是尝试直接转换。在解密方法中,在解密字节数组之前,将
Base64
字符串转换回字节数组


另外,不要使用
getBytes()
,因为输出取决于系统默认值。使用
getBytes(“utf-8”)
或其他任何方法。这消除了歧义。

当您使用AES加密字符串时,您会得到一个字节数组。尝试将这些字节直接转换为字符串(
新字符串(cipher.doFinal(plaintextBytes))
)将导致各种问题。如果要求加密方法的输出为字符串,请使用
Base64
,而不是尝试直接转换。在解密方法中,在解密字节数组之前,将
Base64
字符串转换回字节数组


另外,不要使用
getBytes()
,因为输出取决于系统默认值。使用
getBytes(“utf-8”)
或其他任何方法。这消除了歧义。

为了防止有人感兴趣(或懒得做他们的研究),以下是我在公认答案和评论的帮助下整理的
AES-256
加密和解密的结果代码:

public class TokenEncryptor {

    private final static String TOKEN_KEY = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw";

    public static String encrypt(String plain) {
        try {
            byte[] iv = new byte[16];
            new SecureRandom().nextBytes(iv);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
            byte[] cipherText = cipher.doFinal(plain.getBytes("utf-8"));
            byte[] ivAndCipherText = getCombinedArray(iv, cipherText);
            return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP);
        } catch (Exception e) {
            Ln.e(e);
            return null;
        }
    }

    public static String decrypt(String encoded) {
        try {
            byte[] ivAndCipherText = Base64.decode(encoded, Base64.NO_WRAP);
            byte[] iv = Arrays.copyOfRange(ivAndCipherText, 0, 16);
            byte[] cipherText = Arrays.copyOfRange(ivAndCipherText, 16, ivAndCipherText.length);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
            return new String(cipher.doFinal(cipherText), "utf-8");
        } catch (Exception e) {
            Ln.e(e);
            return null;
        }
    }

    private static byte[] getCombinedArray(byte[] one, byte[] two) {
        byte[] combined = new byte[one.length + two.length];
        for (int i = 0; i < combined.length; ++i) {
            combined[i] = i < one.length ? one[i] : two[i - one.length];
        }
        return combined;
    }

}
公共类令牌加密器{
私有最终静态字符串令牌\u KEY=“FQJFDZGDVDFWBEDKSUGTY3VZ9TAXMvw”;
公共静态字符串加密(普通字符串){
试一试{
字节[]iv=新字节[16];
新SecureRandom().nextBytes(iv);
Cipher Cipher=Cipher.getInstance(“AES/CBC/PKCS5Padding”);
cipher.init(cipher.ENCRYPT_模式,新的SecretKeySpec(TOKEN_KEY.getBytes(“utf-8”),“AES”),新的IvParameterSpec(iv));
byte[]cipherText=cipher.doFinal(plain.getBytes(“utf-8”);
字节[]ivAndCipherText=getCombinedArray(iv,密文);
返回Base64.encodeToString(IvandChiperText,Base64.NO_WRAP);
}捕获(例外e){
Ln.e(e);
返回null;
}
}
公共静态字符串解密(字符串编码){
试一试{
字节[]ivAndCipherText=Base64.decode(编码,Base64.NO_WRAP);
字节[]iv=Arrays.copyOfRange(IvandChiperText,0,16);
字节[]密文=Arrays.copyOfRange(IvandChiperText,16,IvandChiperText.length);
Cipher Cipher=Cipher.getInstance(“AES/CBC/PKCS5Padding”);
cipher.init(cipher.DECRYPT_模式,新的SecretKeySpec(TOKEN_KEY.getBytes(“utf-8”),“AES”),新的IvParameterSpec(iv));
返回新字符串(cipher.doFinal(密文),“utf-8”);
}捕获(例外e){
Ln.e(e);
返回null;
}
}
专用静态字节[]getCombinedArray(字节[]1,字节[]2){
字节[]组合=新字节[1.length+2.length];
对于(int i=0;i
以防有人感兴趣(或懒得做研究),以下是我在公认答案和评论的帮助下整理的
AES-256
加密和解密的结果代码:

public class TokenEncryptor {

    private final static String TOKEN_KEY = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw";

    public static String encrypt(String plain) {
        try {
            byte[] iv = new byte[16];
            new SecureRandom().nextBytes(iv);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
            byte[] cipherText = cipher.doFinal(plain.getBytes("utf-8"));
            byte[] ivAndCipherText = getCombinedArray(iv, cipherText);
            return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP);
        } catch (Exception e) {
            Ln.e(e);
            return null;
        }
    }

    public static String decrypt(String encoded) {
        try {
            byte[] ivAndCipherText = Base64.decode(encoded, Base64.NO_WRAP);
            byte[] iv = Arrays.copyOfRange(ivAndCipherText, 0, 16);
            byte[] cipherText = Arrays.copyOfRange(ivAndCipherText, 16, ivAndCipherText.length);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
            return new String(cipher.doFinal(cipherText), "utf-8");
        } catch (Exception e) {
            Ln.e(e);
            return null;
        }
    }

    private static byte[] getCombinedArray(byte[] one, byte[] two) {
        byte[] combined = new byte[one.length + two.length];
        for (int i = 0; i < combined.length; ++i) {
            combined[i] = i < one.length ? one[i] : two[i - one.length];
        }
        return combined;
    }

}
公共类令牌加密器{
私有最终静态字符串令牌\u KEY=“FQJFDZGDVDFWBEDKSUGTY3VZ9TAXMvw”;
公共静态字符串加密(普通字符串){
试一试{
字节[]iv=新字节[16];
新SecureRandom().nextBytes(iv);
Cipher Cipher=Cipher.getInstance(“AES/CBC/PKCS5Padding”);
cipher.init(cipher.ENCRYPT_模式,新的SecretKeySpec(TOKEN_KEY.getBytes(“utf-8”),“AES”),新的IvParameterSpec(iv));
byte[]cipherText=cipher.doFinal(plain.getBytes(“utf-8”);
字节[]ivAndCipherText=getCombinedArray(iv,密文);
返回Base64.encodeToString(IvandChiperText,Base64.NO_WRAP);
}捕获(例外e){
Ln.e(e);
返回null;
}
}
公共静态字符串解密(字符串编码){
试一试{
字节[]ivAndCipherText=Base64.decode(编码,Base64.NO_WRAP);
字节[]iv=Arrays.copyOfRange(IvandChiperText,0,16);
字节[]密文=Arrays.copyOfRange(IvandChiperText,16,IvandChiperText.length);
Cipher Cipher=Cipher.getInstance(“AES/CBC/PKCS5Padding”);
cipher.init(cipher.DECRYPT_模式,新的SecretKeySpec(TOKEN_KEY.getBytes(“utf-8”),“AES”),新的IvParameterSpec(iv));
返回新字符串(cipher.doFinal(密文),“utf-8”);
}捕获(例外e){
Ln.e(e);
返回null;
}
}
专用静态字节[]getCombinedArray(字节[]1,字节[]2){
字节[]组合=新字节[1.length+2.length];
对于(int i=0;i
这是Artjom B answer的扩展,为我工作

public String encryptMsg(String message, SecretKey secret)
            throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
        Cipher cipher = null;
        cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret);
        byte[] cipherText = cipher.doFinal(message.getBytes("UTF-8"));
        return Base64.encodeToString(cipherText, Base64.NO_WRAP);
    }

public String decryptMsg(String cipherText, SecretKey secret)
        throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
    Cipher cipher = null;
    cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret);
    byte[] decode = Base64.decode(cipherText, Base64.NO_WRAP);
    String decryptString = new String(cipher.doFinal(decode), "UTF-8");
    return decryptString;
}

这是Artjom B answer的扩展,为m工作