Given final block not properly padded

JavaExceptionEncryptionCryptographyjavax.crypto

Java Problem Overview


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.
		}
	}
}

Java Solutions


Solution 1 - Java

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

Solution 2 - Java

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

Solution 3 - Java

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.

Solution 4 - Java

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.

Solution 5 - Java

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.... :)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAltrimView Question on Stackoverflow
Solution 1 - JavaPaŭlo EbermannView Answer on Stackoverflow
Solution 2 - JavaSween WolfView Answer on Stackoverflow
Solution 3 - JavafpacificiView Answer on Stackoverflow
Solution 4 - JavaBejondView Answer on Stackoverflow
Solution 5 - JavaChethana UdatiyawalageView Answer on Stackoverflow