Java AES Encryption with CBC and PKCS7Padding

aesencryptionjavapkcs#7

I have been struggling with this for a couple of days now. I'm required to consume an API that takes an encrypted parameter. The API was written in C#. The encryption requested is the following:


Algorithm: AES
Cipher mode: CBC
Padding mode: PKCS7
Block size: 128
Key size: 256

Key: String –> The key is generated by converting a provided string to a byte array of size 32: Encoding.ASCII.GetBytes(…). The API states that the String is generated by them using MD5 hashing function of a string.

IV: IV array is generated by converting a provided string to a byte array of size 16: Encoding.ASCII.GetBytes(…).

Representation of encrypted string: Base64


After searching and trying so many things that were suggested online, I'm still unable to produce the same encrypted value (Specially that PKCS7 is not supported by default and PKCS5 should be working the same, but it's not). Here are some things that I've tried:
1) Using bouncy castle jar to use PKCS7
2) Adding JCE compliance to be able to remove the limit on key and block sizes.

After contacting them, they sent me an android snippet that's working (which if I run in plain java 8 complains about the provider (NoSuchAlgorithmException: Cannot find any provider supporting AES/CBC/PKCS7Padding)):

public static String encrypt(String value) {
        String plainText = value;
        String escapedString;
        try {
            byte[] key = ENCRYPT_KEY.getBytes("UTF-8");
            byte[] ivs = ENCRYPT_IV.getBytes("UTF-8");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
            AlgorithmParameterSpec paramSpec = new IvParameterSpec(ivs);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
            escapedString = Base64.encodeToString(cipher.doFinal(plainText.getBytes("UTF-8")), Base64.DEFAULT).trim();

            return escapedString;
        } catch (Exception e) {
            e.printStackTrace();
            return value;
        }
    }  

Please any help would be really appreciated.

Here's a code snippet from what I tried:

package com.melhem.TestJava;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class StringFunc {


    final static String key = "API_KEY_32_CHARs";
    final static String iv = "API_IV_16_CHARs";
    final static String algorithm = "AES/CBC/PKCS7Padding";
    private static Cipher cipher = null;
    private static SecretKeySpec skeySpec = null;
    private static IvParameterSpec  ivSpec = null;

    public static void main(String[] args) {
        System.out.println(encrypt("STRING_TO_ENCODE"));
    }

    private static void setUp(){
        try{
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 
            skeySpec = new SecretKeySpec(key.getBytes("ASCII"), "AES");
            ivSpec = new IvParameterSpec(iv.getBytes("ASCII"));
            cipher = Cipher.getInstance(algorithm);
        }catch(NoSuchAlgorithmException | NoSuchPaddingException ex){

            ex.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public static String encrypt(String str){
        try{
//            Integer strL = (int) Math.ceil(str.length() / 8.0);
//            Integer strB = strL*8;
//            str = padRight(str, ' ', strB);
            setUp();
            try {
                cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);
                System.out.println("Block size: " + cipher.getBlockSize() * 8);
                System.out.println("Algorithm name: " + cipher.getAlgorithm());
                System.out.println("Key size: " + skeySpec.getEncoded().length * 8);
            } catch (InvalidAlgorithmParameterException ex) {
                ex.printStackTrace();
                return "";
            }
            byte[] enc = cipher.doFinal(str.getBytes("ASCII"));
            String s = new String(Base64.getEncoder().encode(enc));
            s = s.replace("+", "__plus__");
            s = s.replace("/", "__slash__");
            return s;
        }catch(InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex){
            ex.printStackTrace();
            return "";            
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return "";
        }
    }

    public static String padRight(String msg, char x, int l) {
        String result = "";
        if (!msg.isEmpty()) {
            for (int i=0; i<(l-msg.length()); i++) {
                result = result + x;
            }
            result = msg + result;
        }
        return result;
    }
}

Best Solution

Java Cipher package only supports PKCS#7 padding with AES/CBC/PKCS5Padding. This is not a good naming since PKCS#5 padding supports 8-byte block sizes as DES and PKCS#7 supports up to 255 bytes. For Java use this;

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

The #5 and #7 are not interchangeable for the most modern block ciphers as AES is a 128-bit block cipher. See the question on Crypto.StackExchange.

and, for using AES with 256-bit key size;

Java standard cipher library limited to 128-bit key size. You must go and download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6