优惠及;javax.crypto.BadPaddingException:给定的最后一个块未正确填充

优惠及;javax.crypto.BadPaddingException:给定的最后一个块未正确填充,java,encryption,aes,preferences,Java,Encryption,Aes,Preferences,我已经盯着这个代码看了好几个小时了,我一定错过了一些愚蠢的东西。因此,非常感谢这里有人的帮助 下面是示例代码,这是我能得到的最简单的代码,用于重新编写此问题。代码在第一次运行时工作正常,但在为IV创建首选项之后,它在之后失败 我真正的代码在首选项中的存储比这一个多得多,我在麻烦的拍摄中对其进行了大量简化,使其更易于隔离 我的目标是创建一个类,允许我使用从用户提供的密码派生的密钥存储使用AES 128加密的首选项。e、 g.安全偏好。在故障排除中,我取出了所有用户提供的pwd材料 package

我已经盯着这个代码看了好几个小时了,我一定错过了一些愚蠢的东西。因此,非常感谢这里有人的帮助

下面是示例代码,这是我能得到的最简单的代码,用于重新编写此问题。代码在第一次运行时工作正常,但在为IV创建首选项之后,它在之后失败

我真正的代码在首选项中的存储比这一个多得多,我在麻烦的拍摄中对其进行了大量简化,使其更易于隔离

我的目标是创建一个类,允许我使用从用户提供的密码派生的密钥存储使用AES 128加密的首选项。e、 g.安全偏好。在故障排除中,我取出了所有用户提供的pwd材料

package com.test;

import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidParameterSpecException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import org.apache.commons.codec.binary.Base64;

public class Settings {
    private Preferences prefs = null;
    private byte[] iv = null;
    private SecretKey secret = null;
    Cipher cipher = null;

    public static void main(String[] args){
       Settings t = new Settings();
       String encText = t.encryptText("HELLO");//Encrypt a value
       String output = t.decryptText(encText);//Decrypt the value
       System.out.println(output); //Display the decrypted value.
    }

    public Settings(){
        try {
            String parentClass = new Exception().getStackTrace()[1].getClassName();//Really only controls where the prefs go, shouldn't matter.
            this.prefs = Preferences.userNodeForPackage(Class.forName(parentClass));
            Random r = new SecureRandom();
            KeyGenerator keyGen = KeyGenerator.getInstance("AES");
            keyGen.init(128); // 128 bit key
            this.secret = keyGen.generateKey();

            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
    }
        private String encryptText(String plainText){
        try {
            cipher.init(Cipher.ENCRYPT_MODE, this.secret);
            AlgorithmParameters params = cipher.getParameters();

            this.iv = prefs.getByteArray("IV", null);
            if(this.iv == null){
                this.iv = params.getParameterSpec(IvParameterSpec.class).getIV();
                prefs.putByteArray("IV", this.iv);
            }
            byte[] ciphertext = cipher.doFinal(plainText.getBytes("UTF-8"));
            String ret = new String(Base64.encodeBase64(ciphertext));
            return ret;
        } catch (InvalidParameterSpecException | IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
        return "";
   }
    private String decryptText(String cipherText){
        try {
            this.iv = prefs.getByteArray("IV", null);
            byte[] cText = Base64.decodeBase64(cipherText); 
            cipher.init(Cipher.DECRYPT_MODE, this.secret, new IvParameterSpec(this.iv));
            String ret = new String(cipher.doFinal(cText), "UTF-8");
            return ret;
        } catch (IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException | InvalidAlgorithmParameterException ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
        return "";
   }
}
仅在2次以上运行时接收的Stacktrace:

Feb 07, 2015 9:02:46 PM com.test.Settings decryptText

SEVERE: null
javax.crypto.BadPaddingException: Given final block not properly padded
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313)
    at javax.crypto.Cipher.doFinal(Cipher.java:2087)
    at com.test.Settings.decryptText(Settings.java:77)
    at com.test.Settings.main(Settings.java:34)
------------编辑正确答案------------ 正如GregS指出的那样,当首选项存在时,我没有将IV加载到加密例程中,因此存在不匹配。下面是更新的加密函数,该函数修复了该问题

    private String encryptText(String plainText){
        try {
            this.iv = prefs.getByteArray("IV", null);
            if(this.iv == null) { //If not set, set the IV
                cipher.init(Cipher.ENCRYPT_MODE, this.secret);
                AlgorithmParameters params = cipher.getParameters();
                this.iv = params.getParameterSpec(IvParameterSpec.class).getIV();
                prefs.putByteArray("IV", this.iv);
            } else {
                cipher.init(Cipher.ENCRYPT_MODE, this.secret, new IvParameterSpec(this.iv));
            }

            byte[] ciphertext = cipher.doFinal(plainText.getBytes("UTF-8"));
            String ret = new String(Base64.encodeBase64(ciphertext));
            return ret;
        } catch (InvalidParameterSpecException | IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidAlgorithmParameterException ex) {
            Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
        }
        return "";
   }
private String encryptText(String plainText){
    try {
        this.iv = prefs.getByteArray("IV", null);
        if(this.iv == null) { //If not set, set the IV
            cipher.init(Cipher.ENCRYPT_MODE, this.secret);
            AlgorithmParameters params = cipher.getParameters();
            this.iv = params.getParameterSpec(IvParameterSpec.class).getIV();
            prefs.putByteArray("IV", this.iv);
        } else {
            cipher.init(Cipher.ENCRYPT_MODE, this.secret, new IvParameterSpec(this.iv));
        }

        byte[] ciphertext = cipher.doFinal(plainText.getBytes("UTF-8"));
        String ret = new String(Base64.encodeBase64(ciphertext));
        return ret;
    } catch (InvalidParameterSpecException | IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException ex) {
        Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidAlgorithmParameterException ex) {
        Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
    }
    return "";
   }

正如GregS指出的那样,当首选项存在时,我没有将IV加载到加密例程中,因此存在不匹配。下面是更新的加密函数,该函数修复了该问题

    private String encryptText(String plainText){
        try {
            this.iv = prefs.getByteArray("IV", null);
            if(this.iv == null) { //If not set, set the IV
                cipher.init(Cipher.ENCRYPT_MODE, this.secret);
                AlgorithmParameters params = cipher.getParameters();
                this.iv = params.getParameterSpec(IvParameterSpec.class).getIV();
                prefs.putByteArray("IV", this.iv);
            } else {
                cipher.init(Cipher.ENCRYPT_MODE, this.secret, new IvParameterSpec(this.iv));
            }

            byte[] ciphertext = cipher.doFinal(plainText.getBytes("UTF-8"));
            String ret = new String(Base64.encodeBase64(ciphertext));
            return ret;
        } catch (InvalidParameterSpecException | IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidAlgorithmParameterException ex) {
            Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
        }
        return "";
   }
private String encryptText(String plainText){
    try {
        this.iv = prefs.getByteArray("IV", null);
        if(this.iv == null) { //If not set, set the IV
            cipher.init(Cipher.ENCRYPT_MODE, this.secret);
            AlgorithmParameters params = cipher.getParameters();
            this.iv = params.getParameterSpec(IvParameterSpec.class).getIV();
            prefs.putByteArray("IV", this.iv);
        } else {
            cipher.init(Cipher.ENCRYPT_MODE, this.secret, new IvParameterSpec(this.iv));
        }

        byte[] ciphertext = cipher.doFinal(plainText.getBytes("UTF-8"));
        String ret = new String(Base64.encodeBase64(ciphertext));
        return ret;
    } catch (InvalidParameterSpecException | IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException ex) {
        Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidAlgorithmParameterException ex) {
        Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex);
    }
    return "";
   }

当删除IV参数进行解密(并完全删除IV处理)时会发生什么情况?这将使用默认IV进行加密和解密。也许问题在于首选项不是空的。注意,如果
encryptText
中的首选项不是空的,那么您最终会从首选项中检索到一个iv,但不会在cipher实例中设置iv。但是,在
decryptText
中,您确实在密码实例中设置了iv。因此,加密端使用默认的IV(可能全部为零),而解密端使用从preferences获得的IV。GregS,您完全正确。如果未设置pref,则使用IV,但如果未设置IV,则我从未加载该命令以供使用。这正是我的问题。现在你指出了这一点,这似乎很明显,但我盯着代码看了好几个小时。谢谢你的帮助!如果你想发布答案,我很乐意接受。@Doug我很高兴你解决了你的问题。但是,请不要在您自己的问题文本中添加解决方案。请发表你自己的答案。@Duncan我给了GregS一个发表答案的机会,因为他在评论中给了我答案。如果他第二天不发帖,我会把答案贴出来接受。