2

I am creating an application which calls a third party API, they provided PHP code and what we require is a Java code.

The PHP code is given below:

function encryptIt( $string ) {
$key = 'qJB0rGtIn5UB1xG03efyCp';
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$encrypted = openssl_encrypt($string, 'aes-256-cbc', $key, 0, $iv);
return base64_encode($encrypted . '::' . $iv);
}

Now I changed the abouve code to java as:

private static String openssl_encrypt(String data, String strKey, String strIv) throws Exception {
        SecureRandom randomSecureRandom = new SecureRandom();
        Base64 base64 = new Base64();
        Cipher ciper = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] ivv = new byte[ciper.getBlockSize()];
        randomSecureRandom.nextBytes(ivv);
        SecretKeySpec key = new SecretKeySpec(strKey.getBytes(), "AES");
        IvParameterSpec iv = new IvParameterSpec(ivv, 0, ciper.getBlockSize());

        // Encrypt
        ciper.init(Cipher.ENCRYPT_MODE, key, iv);
        byte[] encryptedCiperBytes = ciper.doFinal(data.getBytes());

        String s = new String(base64.encode(encryptedCiperBytes));
        System.out.println("Ciper : " + s);
        return s;
    }

Actually I am not good in encryption techniques so the PHP code creates a IV code but after searching in internet I tried to create a IV code as above,

But I got the error as:

java.security.InvalidKeyException: Invalid AES key length: 22 bytes
    at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:509)
    at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1067)
    at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1038)
    at javax.crypto.Cipher.init(Cipher.java:1393)
    at javax.crypto.Cipher.init(Cipher.java:1327)

Please help me to solve this

UPDATED:

I updated the code to :

private static String openssl_encrypt(String data) throws Exception {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[16];
        random.nextBytes(salt);
        KeySpec spec = new PBEKeySpec("qJB0rGtIn5UB1xG03efyCp".toCharArray(), salt, 65536, 256); // AES-256
        SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        byte[] key = f.generateSecret(spec).getEncoded();
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");

        byte[] ivBytes = new byte[16];
        random.nextBytes(ivBytes);
        IvParameterSpec iv = new IvParameterSpec(ivBytes);

        Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
        c.init(Cipher.ENCRYPT_MODE, keySpec, iv);
        byte[] encValue = c.doFinal(data.getBytes());

        byte[] finalCiphertext = new byte[encValue.length+2*16];
        System.arraycopy(ivBytes, 0, finalCiphertext, 0, 16);
        System.arraycopy(salt, 0, finalCiphertext, 16, 16);
        System.arraycopy(encValue, 0, finalCiphertext, 32, encValue.length);
        String s = new String(Base64.getEncoder().encodeToString(finalCiphertext));
        return s;
    }

Still i got the error as :

java.security.InvalidKeyException: Illegal key size

My API providers says that thats the key!

Dillz
  • 197
  • 12
  • 1
    How do you call openssl_encrypt ? share the code that calls – azro Jan 04 '19 at 08:20
  • 1
    In the Java code, which Base64 are you using ? (look imports) – azro Jan 04 '19 at 08:23
  • 4
    Possible duplicate of [How to fix Invalid AES key length?](https://stackoverflow.com/questions/29354133/how-to-fix-invalid-aes-key-length) – azro Jan 04 '19 at 08:26
  • @azro : import org.apache.xmlbeans.impl.util.Base64; – Dillz Jan 04 '19 at 09:06
  • @azro : encrypt = openssl_encrypt("","",""); – Dillz Jan 04 '19 at 09:07
  • Please just look the link of duplicate, the answer is the same ;) – azro Jan 04 '19 at 09:19
  • @azro : that link also didnt help me. still i got error `java.security.InvalidKeyException: Illegal key size` – Dillz Jan 04 '19 at 09:32
  • Make sure that the length is correct, and then pad the key to a legal length. potentially your php library does that behind the scenes... dig into the documentation – JoSSte Jan 04 '19 at 09:44
  • @JoSSte : How could i do that , the given key size is 22 bits. That is built in php functions used! – Dillz Jan 04 '19 at 09:50
  • For me, the last code works, with `String s = new String(Base64.encode(finalCiphertext));` at the end, I got a result and no error : https://pastebin.com/4azizJRp – azro Jan 04 '19 at 10:09
  • Unrelated, `return base64_encode($encrypted . '::' . $iv);` is also flawed. It is entirely possible for the ciphertext and IV to contain the double colon character: `:`. Usually the IV is prefixed. If you use a separator, use it **after** base 64 encoding. Your current code may fail *now and then*, and have fun debugging that when it happens. You need a professional protocol design **now**. – Maarten Bodewes Jan 04 '19 at 11:05
  • 1
    @MaartenBodewes: actually `openssl_encrypt` with `options=0` returns the ciphertext in base64, which the posted code then base64's again, which is silly and wasteful but does parse-from-the-left correctly. `$b64ctext . '::' . base64_encode($iv)` would be _better_, and the other order probably better yet. – dave_thompson_085 Jan 04 '19 at 11:44
  • Thanks, I stand corrected! I forgot that PHP doesn't just allow any kind of stuff to be used as input, but also as output. And that `0` indicates options. And that `0` doesn't mean "no encoding" but "base 64". Um, for some reason I don't feel too responsible for that mistake :) Note that double encoding the base 64 ciphertext *is not in the Java code*! – Maarten Bodewes Jan 04 '19 at 11:59
  • The new error might be related to the limited strength cryptography policy which was the default in Java 8 update 152 and earlier, see https://stackoverflow.com/questions/3862800/invalidkeyexception-illegal-key-size – Mark Rotteveel Jan 04 '19 at 12:44

1 Answers1

3

Just like the ill-fated mcrypt crap, openssl_encrypt allows incorrectly sized keys. Instead of rejecting such keys, it simply fills the rest of the key with zero valued bytes ("\0" in PHP). This is completely against any good cryptographic practice, by almost any library for higher level languages.

This is very likely due to two reasons:

  • C, which commonly expects a pointer to a key, for which the size is predetermined (hence the -256 in the openssl commands).
  • PHP, having dynamic variable typing, making users expect functions to accept any kind of crap variable type or state.

To copy the flawed PHP code you therefore have to pad the key with zero bytes (until the array is 32 bytes long). In Java, that's commonly performed using the horrible Arrays.copyOf nowadays (as it also right-pads zero bytes).

Note that the IV will of course still make sure that your encryption results never match. Instead, try to decrypt in Java and use the same scheme for encryption, making sure the IV stays random in the final code.


Keys are never strings. Keys consist of bits or - preferably - references to keys stored somewhere in secure memory. Passwords are strings however, and using PKBDF2 should indeed be preferred.

Of course, to use PBKDF2 it is required to:

  1. need to do the same thing in your PHP code and
  2. make sure you either use an up-to-data Java environment (strongly preferred for secure environments), or install the unlimited jurisdiction policy files in your Java runtime environment.
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • 1
    `Arrays.copyOf` is horrible because it provides two use cases at once: shrinking an array by truncation and expanding an array with zero bytes (and never throwing an error when shrinking or expanding is not needed). It only creates a copy if that copy has exactly the same number of elements (of course). But at least it is well defined, and it does the same thing as the key handling in PHP `mcrypt` and `openssl` functions. – Maarten Bodewes Jan 04 '19 at 10:50
  • Java AES with key 256 bits, even if it has less entropy (or none!), requires the 'unlimited policy' below 8u151 with or without PBKDF2 – dave_thompson_085 Jan 04 '19 at 11:47