2
votes

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;
    }
}
1
Here what's missing from your question: the Java code that you claim you tried but didn't work. - President James K. Polk
Well, this code won't produce the exception you claim to be getting elsewhere. Please have a read of How to create a Minimal, Complete, and Verifiable example. An MCVE is the way to get your problems solved on stackoverflow. - President James K. Polk
Your issue is not about padding. In Java, PKCS7 padding is called PKCS5 (note that AES with PKCS5 padding is impossible). The issue is most likely related to that "MD5 hashing" and string encoding. If you provide some sample input and expected output, maybe we can reverse-engineer it for you. Oh and mention your exact java version number (e.g. 1.8.161). - rustyx

1 Answers

1
votes

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