144

I am trying to implement password based encryption algorithm, but I get this exception:

javax.crypto.BadPaddingException: Given final block not properly padded

What might be the problem?

Here is my code:

public class PasswordCrypter {

    private Key key;

    public PasswordCrypter(String password)  {
        try{
            KeyGenerator generator;
            generator = KeyGenerator.getInstance("DES");
            SecureRandom sec = new SecureRandom(password.getBytes());
            generator.init(sec);
            key = generator.generateKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public byte[] encrypt(byte[] array) throws CrypterException {
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch (Exception e) { 
            e.printStackTrace();
        }
        return null;
    }

    public byte[] decrypt(byte[] array) throws CrypterException{
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch(Exception e ){
            e.printStackTrace();
        }
        return null;
    }
}

(The JUnit Test)

public class PasswordCrypterTest {

    private static final byte[] MESSAGE = "Alpacas are awesome!".getBytes();
    private PasswordCrypter[] passwordCrypters;
    private byte[][] encryptedMessages;

    @Before
    public void setUp() {
        passwordCrypters = new PasswordCrypter[] {
            new PasswordCrypter("passwd"),
            new PasswordCrypter("passwd"),
            new PasswordCrypter("otherPasswd")
        };

        encryptedMessages = new byte[passwordCrypters.length][];
        for (int i = 0; i < passwordCrypters.length; i++) {
            encryptedMessages[i] = passwordCrypters[i].encrypt(MESSAGE);
        }
    }

    @Test
    public void testEncrypt() {
        for (byte[] encryptedMessage : encryptedMessages) {
            assertFalse(Arrays.equals(MESSAGE, encryptedMessage));
        }

        assertFalse(Arrays.equals(encryptedMessages[0], encryptedMessages[2]));
        assertFalse(Arrays.equals(encryptedMessages[1], encryptedMessages[2]));
    }

    @Test
    public void testDecrypt() {
        for (int i = 0; i < passwordCrypters.length; i++) {
            assertArrayEquals(MESSAGE, passwordCrypters[i].decrypt(encryptedMessages[i]));
        }

        assertArrayEquals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[1]));
        assertArrayEquals(MESSAGE, passwordCrypters[1].decrypt(encryptedMessages[0]));

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[2])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[2].decrypt(encryptedMessages[1])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }
    }
}
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Altrim
  • 6,536
  • 4
  • 33
  • 36

7 Answers7

233

If you try to decrypt PKCS5-padded data with the wrong key, and then unpad it (which is done by the Cipher class automatically), you most likely will get the BadPaddingException (with probably of slightly less than 255/256, around 99.61%), because the padding has a special structure which is validated during unpad and very few keys would produce a valid padding.

So, if you get this exception, catch it and treat it as "wrong key".

This also can happen when you provide a wrong password, which then is used to get the key from a keystore, or which is converted into a key using a key generation function.

Of course, bad padding can also happen if your data is corrupted in transport.

That said, there are some security remarks about your scheme:

  • For password-based encryption, you should use a SecretKeyFactory and PBEKeySpec instead of using a SecureRandom with KeyGenerator. The reason is that the SecureRandom could be a different algorithm on each Java implementation, giving you a different key. The SecretKeyFactory does the key derivation in a defined manner (and a manner which is deemed secure, if you select the right algorithm).

  • Don't use ECB-mode. It encrypts each block independently, which means that identical plain text blocks also give always identical ciphertext blocks.

    Preferably use a secure mode of operation, like CBC (Cipher block chaining) or CTR (Counter). Alternatively, use a mode which also includes authentication, like GCM (Galois-Counter mode) or CCM (Counter with CBC-MAC), see next point.

  • You normally don't want only confidentiality, but also authentication, which makes sure the message is not tampered with. (This also prevents chosen-ciphertext attacks on your cipher, i.e. helps for confidentiality.) So, add a MAC (message authentication code) to your message, or use a cipher mode which includes authentication (see previous point).

  • DES has an effective key size of only 56 bits. This key space is quite small, it can be brute-forced in some hours by a dedicated attacker. If you generate your key by a password, this will get even faster. Also, DES has a block size of only 64 bits, which adds some more weaknesses in chaining modes. Use a modern algorithm like AES instead, which has a block size of 128 bits, and a key size of 128 bits (for the most common variant, variants for 196 and 256 also exist).

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
  • 1
    I just want to confirm. I'm new to encryption and this is my scenario, I'm using AES encryption. in my encrypt/decrypt function, I'm using an encryption key. I used a wrong encryption key in decrypt and I got this `javax.crypto.BadPaddingException: Given final block not properly padded`. Should I treat this as a wrong key? – kenicky Apr 16 '15 at 11:13
  • Just to be clear, this can also happen when providing the wrong password for a key store file, such as a .p12 file, which is what just happened to me. – Warren Dew Sep 17 '15 at 17:43
  • 2
    @WarrenDew "Wrong password for a key store file" is just a special case of "wrong key". – Paŭlo Ebermann Sep 17 '15 at 21:07
  • @kenicky sorry, I saw your comment just now ... yes, a wrong key almost always causes this effect. (Of course, corrupted data is another possibility.) – Paŭlo Ebermann Sep 17 '15 at 21:09
  • @PaŭloEbermann I agree, but I don't think that's necessarily immediately obvious, since it's different than the situation in the original post where the programmer has control over the key and decryption. I did find your answer useful enough to upvote it, though. – Warren Dew Sep 18 '15 at 00:35
  • 1
    FYI. This is also a popular error to get when you have converted an Android signing key to `PKCS12` format using *`keytool`*. – not2qubit Apr 02 '19 at 13:02
  • Is there any possibilities of using different random.generateSeed(16) for encryption and decryption. Because we got same error while decryption @PaŭloEbermann. – mvm May 06 '20 at 10:29
  • @mvm I have no clue what you are doing, but decryption should not depend on any randomness. – Paŭlo Ebermann May 07 '20 at 12:06
  • I've encrypt the pwd using random.generateSeed(16) and stored it property file. While starting the application, we are calling decryption method using different random number. That time we got this padding error. Is there any possibilities for encrypt and decrypt using different random number? @PaŭloEbermann – mvm May 07 '20 at 12:34
  • Encryption and decryption need to use the same key. If it would work with different (or even arbitrary) keys, the cryptosystem is broken. – Paŭlo Ebermann May 07 '20 at 15:17
23

This can also be a issue when you enter wrong password for your sign key.

Sween Wolf
  • 330
  • 2
  • 6
2

depending on the cryptography algorithm you are using, you may have to add some padding bytes at the end before encrypting a byte array so that the length of the byte array is multiple of the block size:

Specifically in your case the padding schema you chose is PKCS5 which is described here: http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_CJ_SYM__PAD.html

(I assume you have the issue when you try to encrypt)

You can choose your padding schema when you instantiate the Cipher object. Supported values depend on the security provider you are using.

By the way are you sure you want to use a symmetric encryption mechanism to encrypt passwords? Wouldn't be a one way hash better? If you really need to be able to decrypt passwords, DES is quite a weak solution, you may be interested in using something stronger like AES if you need to stay with a symmetric algorithm.

fpacifici
  • 523
  • 2
  • 11
  • 1
    so could you please post the code which tries to encrypt/decrypt ? (and check that the byte array you try to decrypt is not bigger than the block size) – fpacifici Nov 08 '11 at 12:11
  • 1
    I am very new to Java and also Cryptography so I still don't know better ways to do encryption. I just want to get this one done than probably look for better ways to implement it. – Altrim Nov 08 '11 at 12:15
  • can you update the link because it doesn't work @fpacifici and I updated my post I included the JUnit test that tests the encryption and decryption – Altrim Nov 08 '11 at 15:36
  • Corrected (sorry copy paste error). Anyway, indeed your issue happens since you decrypt with a key which is not the same as the one used for encryption as explained by Paulo. This happens since the method annotated with @Before in junit is executed before every test method, thus regenerating the key every time. since the key is initialized randomly it will be different each time. – fpacifici Nov 09 '11 at 22:04
1

I met this issue due to operation system, simple to different platform about JRE implementation.

new SecureRandom(key.getBytes())

will get the same value in Windows, while it's different in Linux. So in Linux need to be changed to

SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
kgen.init(128, secureRandom);

"SHA1PRNG" is the algorithm used, you can refer here for more info about algorithms.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Bejond
  • 1,188
  • 1
  • 11
  • 18
0

javax.crypto.BadPaddingException: Given final block not properly padded.

Such issues can arise if a bad key is used during decryption.

For myself this happens when I used wrong key for decrypting. It is always case sensitive.So make sure you have used the same key when you used when encrypting.... :)

0

If you sure all configurations are right, so that may you get this exception due to sending a null value as a ciphered to decript or a plane text to encript.

Shadyar
  • 709
  • 8
  • 16
0

If you get this warning when generating a keystore

Warning:  Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified -keypass value.
Generating 3,072 bit RSA key pair and self-signed certificate (SHA384withRSA) with a validity of 90 days

you may need to remove the property secret in your application properties file.

Just these properties are enough

encrypt:
  keyStore:
    location: <yourFileLocation>
    password: <yourPassword>
    alias: <yourAlias>

It works for me.

emeraldhieu
  • 9,380
  • 19
  • 81
  • 139