badPaddingException on some call of the doFinal. Not all. Same input
Asked Answered
G

7

18

I use a javax.crypto.cipher for encrypt and decrypt some data. It's working well. But sometimes, the decryption faill with a badPaddingException. If i compare a succefull call with a failling call, the input given to the cipher are the same, and the cipher is initialised the same way.

that how i instanciate my cipher

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);

That how i use it

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

The exception is raised on the doFinal.

Any idea ?

Thanks !

Oh, btw, i use bouncyCastle as a provider, and add it on top on the list with

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

For the sake of the completeness, and because the problem still appeart from time to time. Here is the complete class.

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();
    }
}

Edit : I would like to stress this 2 points :

  • the same input will sometime fail / sometime be sucessfully decrypted. ( i know that thanks to the logs in the BadPaddingException )
  • this main calling the decrypt method 1 000 000 000 times doest reproduce the 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() ;
    }
}

So maybe it could come from the way i use the EncryptDecryptUtils class ? It's a field of a Spring bean, instancied one time.

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)
Goby answered 4/2, 2011 at 8:0 Comment(11)
Hmm, that code looks ok to me. The problem must be in some code you are not showing.Pylorus
Greg : all the code is here now.Goby
Things which occur. 1. If you encrypt the same string twice, do you get the same array back? (This is to rule out the possibility that there's a reset method you should be calling). 2. Do you encrypt all the strings which you're trying to decrypt? That is, is this code everything cryptographic? 3. Could it be an IO error elsewhere which is truncating the array before you try to decrypt it?Delectate
Is there a test case that can be reproduced? Hope you must have already seen this, if not refer coderanch.com/t/133352/Security/BadPaddingException-DESMydriasis
@Peter Taylor. yes, if i encrypt the same thing twice, i get the same arrays.Goby
@Peter Taylor. Actually, a failling code of cypher.doFinal() and a succefull one have the exact same byte array in input.Goby
Your example is incorrect: new EncryptDecryptUtil("pass").encrypt("YES") returns "Q3qWLKo6yJY=" and not "cjTMylJXfFQ="Gesner
@Luzifer42. Indeed. I used another passphrase. Response editedGoby
Hi, @AntoineClaval have you fix this issue?Aldridge
@radityagumay , The application was jumping from provider to provider.Goby
@AntoineClaval i was solve this problem then. this because i enable app backup in manifest.Aldridge
P
3

Just an idea: Maybe you are jumping among different providers (Sun/BC/etc)

Regards.

Ph answered 24/2, 2011 at 0:29 Comment(1)
+1, this would explain why the stack trace contained traces of Sun JCE and not BC. I can reproduce the error semi-reliably by scheduling another thread to remove BC after the loop starts executing... perhaps one of your other Spring beans is doing something like this?Downhill
P
3

Some thoughts:

The stack trace indicates that the BouncyCastle provider might not be getting picked up. You can try to pass the provider explicitly to the Cipher and the KeyFactory.

The code you provided is the only code that's running, right? No other threads in the process?

I wonder if you might be confusing the cipher by specifying the salt and the iterationCount both on the KeySpec, and ParameterSpec. The examples I see ( encrypt PBEWithMD5AndDES in j2me, http://cs.saddleback.edu/rwatkins/CS4B/Crypto/FileEncryptor.html), do not specify those when creating the KeySpec.

Pelkey answered 24/2, 2011 at 5:39 Comment(1)
I removed the comment about position of the Provider, because the javadoc says I was wrong and the OP was doing it right...Pelkey
G
1

Maybe it is related to this bug: https://bugs.java.com/bugdatabase/view_bug?bug_id=4414138

Bottom line: Don't use String to store encrypted text!

Maybe this could fix it:

dec = Base64.decode(str.getBytes("UTF-8"));
Gesner answered 21/2, 2011 at 16:16 Comment(3)
Indeed, i saw this thread on the sun website. I dont have really the choice, the encrypted text will come as a string. But i dont think it's the problem, as the same byte arrays. ( dec ) will sometime fail and sometime not.Goby
Can you give us an example plaintext and ciphertext where the Exception is thrown? So we can try to reproduce this. It is unlikely that the same byte array will sometimes fail to decrypt and some times not. this would mean that the decryption differs.Gesner
No, i cant, actually it's almost always the same string who is decrypted. ( a YES/NO string ). As you see, in the catch of the BadPaddingException, i display the encrypted string and the byte array : it's the same with a successfull or failled decryption. It's pretty suspiscious if you ask me... Could it come from a switch in the security provider ? ( i need to use bouncyCastle, that's why he is inserted in first position. But in the failled stackTrace, the standard sun JCE is used ( see stackTrace in edited post ))Goby
E
1

I'm going to go out on a limb and suggest that it's a thread-safety issue. Try adding synchronized blocks around the ecipher/dcipher#doFinal calls, you can easily use the associated Cipher object as an object to synchronize with.

byte[] enc;
synchronized (ecipher) {
      enc = ecipher.doFinal(utf8);
}
Encrinite answered 16/9, 2013 at 23:25 Comment(0)
G
0

This is a bit of a shot in the dark, but try instantiating your cipher like this:

dcipher = Cipher.getInstance("PBEWithMD5AndDES/CBC/PKCS5Padding");
Gink answered 4/2, 2011 at 9:7 Comment(6)
Thanks for the shot... hum, i think i have done the reverse shot :). Let me check SVN... yes. It was like thatGoby
Hum. But the SecretKeyFactory were still intialised with only the Algorithm. Let try that.Goby
No, PBE ciphers specify their own padding and mode.Pylorus
In that case I'm stumped. Any ideas, GregS?Gink
None. Suspiciously little information was given by the OP however.Pylorus
I like you'r "suspiciously" greg :p. Full transparency, i hate to do that, but i posted the whole class. ( i dont like to do that, it's like "solve my problem for me please ... but hey, it's actually the case here )Goby
E
0

badPaddingException usually means the password was wrong. Using "wrong_password" as the password recreates the error:

EncryptDecryptUtil encryptDecript = new EncryptDecryptUtil("wrong_password");
encryptDecript.decrypt("Q3qWLKo6yJY=");

Results:

error during decryption. String to decode was : Q3qWLKo6yJY=
byte array to decode was : [67, 122, -106, 44, -86, 58, -56, 106]
javax.crypto.BadPaddingException: Given final block not properly padded
Eiland answered 24/2, 2011 at 4:54 Comment(0)
M
0

I know this question is old, but I was facing the same problem on a Spring Boot 2.7.12 application. Turns out my cipher object was retaining some cache, I don't know why, so the workaround was to set my cipher class with the @RequestScope from Spring boot annotations. It runs fine now

Malinda answered 6/6, 2023 at 15:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.