badPaddingException on some call of the doFinal.不是全部。相同的输入

2022-09-02 22:33:21

我使用javax.crypto.cipher来加密和解密一些数据。它运行良好。但有时,解密会失败,并带有一个糟糕的PaddingException。如果我将成功调用与失败调用进行比较,则为密码提供的输入是相同的,并且密码以相同的方式初始化。

我如何实例化我的密码

dcipher = Cipher.getInstance("PBEWithMD5AndDES");
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

这就是我如何使用它

dec = Base64.decode(str) ;
byte[] utf8 = dcipher.doFinal(dec);

异常在 doFinal 上引发。

任何想法?

谢谢!

哦,顺便说一句,我使用bouncyCastle作为提供者,并将其添加到列表的顶部

Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);

为了完整性,也因为问题还是时不时出现的。这是完整的类。

public class EncryptDecryptUtil {
    /** Encryption Cipher */
    private static Cipher ecipher;
    /** Decription Cipher */
    private static Cipher dcipher;

    private static Logger logger = Logger.getLogger(EncryptDecryptUtil.class);

    /**
     * Constructor used to create this object. Responsible for setting and initializing this object's encrypter and
     * decrypter Cipher instances given a Secret Key and algorithm.
     * 
     * @param key Secret Key used to initialize both the encrypter and decrypter instances.
     * @param algorithm Which algorithm to use for creating the encrypter and decrypter instances.
     */
    public EncryptDecryptUtil(SecretKey key, String algorithm) {
        Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
        try {
            ecipher = Cipher.getInstance(algorithm);
            dcipher = Cipher.getInstance(algorithm);
            ecipher.init(Cipher.ENCRYPT_MODE, key);
            dcipher.init(Cipher.DECRYPT_MODE, key);
        } catch (NoSuchPaddingException e) {
            System.out.println("EXCEPTION: NoSuchPaddingException");
        } catch (NoSuchAlgorithmException e) {
            System.out.println("EXCEPTION: NoSuchAlgorithmException");
        } catch (InvalidKeyException e) {
            System.out.println("EXCEPTION: InvalidKeyException");
        }
    }

    /**
     * Constructor used to create this object. Responsible for setting and initializing this object's encrypter and
     * decrypter Chipher instances given a Pass Phrase and algorithm.
     * 
     * @param passPhrase Pass Phrase used to initialize both the encrypter and decrypter instances.
     */
    public EncryptDecryptUtil(String passPhrase) {
        Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
        // 8-bytes Salt
        byte[] salt = { (byte) 0xB9, (byte) 0x8B, (byte) 0xD8, (byte) 0x31, (byte) 0x55, (byte) 0x24, (byte) 0xF3, (byte) 0x13 };

        // Iteration count
        int iterationCount = 19;

        try {
            // Generate the secret key associated to the passphrase.
            KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
            SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);

            // Get instance of the cipher
            ecipher = Cipher.getInstance("PBEWithMD5AndDES");
            dcipher = Cipher.getInstance("PBEWithMD5AndDES");

            // Prepare the parameters to the cipthers
            AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

        } catch (InvalidAlgorithmParameterException e) {
            logger.error("during encrypter instantiation",e);
        } catch (InvalidKeySpecException e) {
            logger.error("during encrypter instantiation",e);
        } catch (NoSuchPaddingException e) {
            logger.error("during encrypter instantiation",e);
        } catch (NoSuchAlgorithmException e) {
            logger.error("during encrypter instantiation",e);
        } catch (InvalidKeyException e) {
            logger.error("during encrypter instantiation",e);
        }
    }

    /**
     * Takes a single String as an argument and returns an Encrypted version of that String.
     * 
     * @param str String to be encrypted
     * @return <code>String</code> Encrypted version of the provided String
     */
    public String encrypt(String str) {
        try {
            // Encode the string into bytes using utf-8
            byte[] utf8 = str.getBytes("UTF8");

            // Encrypt
            byte[] enc = ecipher.doFinal(utf8);

            // Encode bytes to base64 to get a string
            return new String( Base64.encode(enc), "UTF8");

        } catch (BadPaddingException e) {
            logger.error("during encryption : ",e);
        } catch (IllegalBlockSizeException e) {
            logger.error("during encryption : ",e);
        } catch (UnsupportedEncodingException e) {
            logger.error("during encryption : ",e);
        } 
        return new String();
    }


    /**
     * Takes a encrypted String as an argument, decrypts and returns the decrypted String.
     * 
     * @param str Encrypted String to be decrypted
     * @return <code>String</code> Decrypted version of the provided String
     */
    public String decrypt(String str) {
        byte[] dec = new byte[0];
        try {
            // Decode base64 to get bytes. Not sure to understand why.
            dec = Base64.decode(str) ;
            // Decrypt
            byte[] utf8 = dcipher.doFinal(dec);
            // Decode using utf-8
            return new String(utf8, "UTF8");

        } catch (BadPaddingException e) {
            logger.error("error during decryption. String to decode was : "+str + " byte array to decode was : "+ Arrays.toString(dec) ,e);
        } catch (IllegalBlockSizeException e) {
            logger.error("during decryption : ",e);
        } catch (UnsupportedEncodingException e) {
            logger.error("during decryption : ",e);
        }  
        return new String();
    }
}

编辑:我想强调这两点:

  • 相同的输入有时会失败/有时会成功解密。( 我知道多亏了 BadPaddingException 中的日志 )
  • 这个主要调用解密方法1 000 000 000次 doest重现issus。

...

for( int i = 0 ; i<1000000000 ; i++){
    EncryptDecryptUtil encryptDecript = new EncryptDecryptUtil("pass");
    if ( !"YES".equals(encryptDecript.decrypt("Q3qWLKo6yJY="))){
        System.out.println("Fail at call " + i);
        throw new InvalidParameterException() ;
    }
}

所以也许它可能来自我使用CryptedDecryptUtils类的方式?这是一片春豆的田野,被烤过一次。

com.myStuff.dlm.cryptography.EncryptDecryptUtil  error during decryption. String to   decode was : Q3qWLKo6yJY= byte array to decode was : [114, 52, -52, -54, 82, 87, 124, 84]
javax.crypto.BadPaddingException: Given final block not properly padded
    at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
    at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
    at com.sun.crypto.provider.SunJCE_ab.b(DashoA13*..)
    at com.sun.crypto.provider.PBEWithMD5AndDESCipher.engineDoFinal(DashoA13*..)
    at javax.crypto.Cipher.doFinal(DashoA13*..)
    at com.dvidea.dlm.cryptography.EncryptDecryptUtil.decrypt(EncryptDecryptUtil.java:166)

答案 1

只是一个想法:也许你正在不同的提供商(Sun/ BC / etc)之间跳跃

问候。


答案 2

一些想法:

堆栈跟踪指示 BouncyCastle 提供程序可能未被选取。您可以尝试将提供程序显式传递给密码和 KeyFactory。

您提供的代码是唯一正在运行的代码,对吧?进程中没有其他线程?

我想知道您是否通过在KeySpec和ParadeSpec上指定盐和迭代计数来混淆密码。我看到的例子(在j2me中加密PBEWithMD5AndDEShttp://cs.saddleback.edu/rwatkins/CS4B/Crypto/FileEncryptor.html),在创建KeySpec时没有指定这些示例。


推荐