javax.crypto.BadPaddingException: Given final block not properly padded java callout apigee

Not applicable
package com.tmobile;
import com.apigee.flow.execution.ExecutionContext;
import com.apigee.flow.execution.ExecutionResult;
import com.apigee.flow.execution.spi.Execution;
import com.apigee.flow.message.MessageContext;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class DecryptionDocumentId2 implements Execution {
    private static final String _varPrefix = "decrypt_";
    private static final String varName(String s) { return _varPrefix + s; }
    @Override
    public ExecutionResult execute(MessageContext messageContext, ExecutionContext executionContext) {
        try {
            // decode the base64 encoded string
            String decryptionKey1 = messageContext.getVariable("dk1_decryption");
            //String decryptionKey2 = messageContext.getVariable("dk2_decryption");
            byte[] key1 = decryptionKey1.getBytes("UTF-8");
            //byte[] key2 = decryptionKey2.getBytes("UTF-8");
            SecretKey secretKey1 = new SecretKeySpec(key1, 0, key1.length, "AES");
            // SecretKey secretKey2 = new SecretKeySpec(key2, 0, key2.length, "AES");
            String documentId = messageContext.getVariable("documentId");
            if (documentId == null) {
                messageContext.setVariable(varName("reason"), "missing documentId");
                return ExecutionResult.ABORT;
            }
            String decryptedValue = decrypt(documentId.trim(),secretKey1,messageContext);
            messageContext.setVariable("decryptedMsisdn", decryptedValue);
            return ExecutionResult.SUCCESS;
        }
        catch (Exception e) {
            String error = e.toString();
            messageContext.setVariable(varName("error"), error);
            int ch = error.lastIndexOf(':');
            if (ch >= 0) {
                messageContext.setVariable(varName("reason"), error.substring(ch+2));
            }
            else {
                messageContext.setVariable(varName("reason"), error);
            }
            return ExecutionResult.ABORT;
        }
    }
    private String decrypt(String encryptedText, SecretKey secretKey, MessageContext messageContext)
        throws Exception {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        byte[] decryptedTextBytes = null;
        try {
            decryptedTextBytes = cipher.doFinal(DatatypeConverter.parseBase64Binary(encryptedText));
        }
        catch (IllegalBlockSizeException e) {
            messageContext.setVariable(varName("reason1"), e.toString());
            throw e;
        }
        catch (BadPaddingException e) {
            messageContext.setVariable(varName("reason2"), e.toString());
            throw e;
        }
        return new String(decryptedTextBytes);
    }
}

This is a java code for an Apigee Edge callout. During execution, while trying to decrypt encrypted data in APIGEE , we are getting error of final block not properly padded.

The code we started with is:

http://aesencryption.net/#Java-aes-encryption-example

We generate encrypted data from there and are trying to decrypt it with a key.

0 2 46.2K
2 REPLIES 2

Yes, encryption and decryption is notoriously difficult to debug. Getting errors like "final block not properly padded" is not particularly helpful. I feel your pain.

Unfortunately I don't have an easy solution for your problem. A padding issue during decryption is absolutely not related to Apigee Edge. It's just crypto challenges.

I recommend that you create some unit tests. It's not difficult to set up mocks of MessageContext for an Apigee Edge callout. This will allow you to run tests of your crypto (encrypt AND decrypt) logic outside of Apigee Edge, which will allow you to iterate faster and get it sorted quickly.

Having said all that, you need to take some care in designing the crypto, the key management, selecting the algorithm. For example, you have AES/ECB/PKCS5Padding in your code, but that's probably not optimal. Just make sure that your callout is serving the requirements you have for the API Proxy.

Crypto is tricky, so I advise you to take your time and get some advice and review.

Hi @akshay.anand9 ,

Please refer below documentation on usage of Cipher class. There are different implementations based on the key sizes provided.

If you check your code and the reference code that you have used for encrypting your string, you'll see that they are creating a message digest and then copying only 128 bits out of that. Here is the code for your reference:

public static void setKey(String myKey){
        MessageDigest sha = null;
        try {
            key = myKey.getBytes("UTF-8");
            System.out.println(key.length);
            sha = MessageDigest.getInstance("SHA-1");
            key = sha.digest(key);
            key = Arrays.copyOf(key, 16); // use only first 128 bit
            System.out.println(key.length);
            System.out.println(new String(key,"UTF-8"));
            secretKey = new SecretKeySpec(key, "AES");
            
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
}

Whereas you are using direct bytes from secreteKey:

// decode the base64 encoded string
String decryptionKey1 = messageContext.getVariable("dk1_decryption");
//String decryptionKey2 = messageContext.getVariable("dk2_decryption");
byte[] key1 = decryptionKey1.getBytes("UTF-8");
//byte[] key2 = decryptionKey2.getBytes("UTF-8");
SecretKey secretKey1 = new SecretKeySpec(key1, 0, key1.length, "AES");

If you change above code to generate a secreteKey with 128 bit as per code mentioned above using MessageDigest your decryption will work fine.

Hope this helps!