java javax.crypto.IllegalBlockSizeException:输入长度不是 16 字节的倍数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43890592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 07:48:11  来源:igfitidea点击:

javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes

javaexceptionaesdecodeencode

提问by rainbow

I have an ArrayList which contains some objects. Object is a container for login/pass.
I try to decode them because i have to serialize them into local file for recreation after another launch.
Problem is during encryption I recieve

我有一个包含一些对象的 ArrayList。对象是登录/通行证的容器。
我尝试对它们进行解码,因为我必须将它们序列化为本地文件,以便在再次启动后重新创建。
问题是在我收到的加密期间

javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
    at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1039)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:983)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:845)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)

And I completely don't understand why. In my opinion Base64 should deal with this. But maybe I don't understand properly its meaning.
I use to encrypt/deccode

我完全不明白为什么。我认为 Base64 应该处理这个问题。但也许我没有正确理解它的含义。
我用来加密/解码

public class Move
{
    private static Move instance;

    String key = "pT5IkWNR90gJo5YM";
    String initVector = "RandomInitVector";
    Cipher cipher;


    private Move()
    {
//      try
//      {
//          cipher = Cipher.getInstance("AES/CBC/NoPadding");
//      }
//      catch (NoSuchAlgorithmException | NoSuchPaddingException e)
//      {
//          e.printStackTrace();
//      }
    }


    public void saveData(ArrayList<Account> dataToSave)
    {
        try
        {
            FileOutputStream fileOut = new FileOutputStream(Config.SERIAL_FILE);
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(encrypt(dataToSave));
            out.close();
            fileOut.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }


    @SuppressWarnings("unchecked")
    public ArrayList<Account> loadData()
    {
        ArrayList<Account> loadedData = new ArrayList<Account>();
        File f = new File(Config.SERIAL_FILE);
        if (f.exists())
        {
            try
            {
                FileInputStream fileIn = new FileInputStream(Config.SERIAL_FILE);
                ObjectInputStream in = new ObjectInputStream(fileIn);
                loadedData = (ArrayList<Account>) in.readObject();
                in.close();
                fileIn.close();
            }
            catch (IOException | ClassNotFoundException e)
            {
                e.printStackTrace();
            }
            loadedData = decrypt(loadedData);
        }
        else
        {
            loadedData = new ArrayList<Account>();
        }
        return loadedData;
    }


    private ArrayList<Account> encrypt(List<Account> decrypted)
    {
        ArrayList<Account> encrypted = new ArrayList<Account>();

        try
        {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            cipher = Cipher.getInstance("AES/CBC/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            for (int i = 0; i < decrypted.size(); i++)
            {
                try
                {
                    byte[] login = cipher.doFinal(Base64.getDecoder().decode(decrypted.get(i).getLogin().getBytes()));
                    encrypted.add(new Account(login.toString(), "pass"));
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
            }
        }
        catch (InvalidKeyException | InvalidAlgorithmParameterException | UnsupportedEncodingException | NoSuchAlgorithmException | NoSuchPaddingException e)
        {
            e.printStackTrace();
        }
        return encrypted;
    }


    private ArrayList<Account> decrypt(List<Account> encrypted)
    {
        ArrayList<Account> decrypted = new ArrayList<Account>();

        try
        {
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            cipher = Cipher.getInstance("AES/CBC/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

            for (int i = 0; i < encrypted.size(); i++)
            {
                byte[] login = cipher.doFinal(Base64.getDecoder().decode(encrypted.get(i).getLogin()));
                decrypted.add(new Account(new String(login), "pass"));
            }
        }
        catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | NoSuchAlgorithmException | NoSuchPaddingException e)
        {
            e.printStackTrace();
        }
        return decrypted;
    }


    public static Move getMove()
    {
        if (instance == null)
        {
            instance = new Move();
        }
        return instance;
    }
}

回答by didiz

AES block size is always 128bit, it must receive input in multiples of this number.

AES 块大小始终为 128 位,它必须以该数字的倍数接收输入。

Smaller input must be padded to 16 bytes, and the type of padding specified to the algorithm.

较小的输入必须填充到 16 个字节,并为算法指定填充类型。

Using "AES/CBC/PKCS5Padding" will do the trick.

使用“AES/CBC/PKCS5Padding”可以解决问题。

cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

If you use NoPadding, then you must implement your own padding for encryption, and make sure to remove it from the resulting string in decryption.

如果您使用 NoPadding,那么您必须实现自己的加密填充,并确保在解密时将其从结果字符串中删除。