javaphpencryptionrijndael

Java code for mcrypt_encrypt PHP function with MCRYPT_RIJNDAEL_256 cipher


I have a PHP code for encrypting a string(token). I need to generate this token and call the legacy backend API. Backend API decrypts this and validates it before allowing access to the API. We are building the client application in Java. So this code needs to be implemented in Java.

$stringToEncode="****String which needs to be encrypted. This string length is multiple of 32********************";
$key="32ByteKey-asdcxzasdsadasdasdasda";
    echo base64_encode(
        mcrypt_encrypt(
                    MCRYPT_RIJNDAEL_256,
                    $key,
                    $stringToEncode,
                    MCRYPT_MODE_CBC,
                    $key
                )
            );

Here IV is same as key. I tried with below Java code using "org.bouncycastle.crypto"

private static void encrypt(String key, String data) throws InvalidCipherTextException {
            
        byte[] givenKey = key.getBytes(Charset.forName("ASCII"));        
        final int keysize = 256;
        byte[] keyData = new byte[keysize / Byte.SIZE];
        System.arraycopy(givenKey, 0, keyData, 0, Math.min(givenKey.length, keyData.length));
        KeyParameter keyParameter = new KeyParameter(keyData);
        BlockCipher rijndael = new RijndaelEngine(256);
        ZeroBytePadding c = new ZeroBytePadding();
        PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);
        CipherParameters ivAndKey = new ParametersWithIV(keyParameter, key.getBytes(Charset.forName("ASCII")));
        pbbc.init(true, ivAndKey);
        byte[] plaintext = data.getBytes(Charset.forName("UTF8"));
        byte[] ciphertext = new byte[pbbc.getOutputSize(plaintext.length)];
        int offset = 0;
        offset += pbbc.processBytes(plaintext, 0, plaintext.length, ciphertext, offset);
        offset += pbbc.doFinal(ciphertext, offset);
        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(ciphertext));
    }

But getting below exception -

Exception in thread "main" java.lang.IllegalArgumentException: invalid parameter passed to Rijndael init - org.bouncycastle.crypto.params.ParametersWithIV

I even tried with "javax.crypto" as shown below -

import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
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 aes{
    
     public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
            String key = "32ByteKey-asdcxzasdsadasdasdasda";
            String data = "****String which needs to be encrypted. This string length is multiple of 32********************";
            encrypt(key, data);
        }
    
    public static void encrypt(String key1, String data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
        
        byte[] key = key1.getBytes(Charset.forName("ASCII"));
        byte[] decrypted = data.getBytes(Charset.forName("UTF8"));      
        
        IvParameterSpec ivSpec = new IvParameterSpec(key);

        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivSpec);
        byte[] encrypted = Base64.getEncoder().encodeToString(cipher.doFinal(decrypted)).getBytes();

        System.out.println(encrypted);
    }   
}

But getting beow exception -

Exception in thread "main" java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long

Can someone please suggest on how can I implement this?


Solution

  • The PHP code uses Rijndael with a blocksize of 256 bits. This isn't supported by the SunJCE Provider, only AES (which is a subset of Rindael with a blocksize of 128 bits). For this reason the second Java code snippet doesn't work either. Therefore a third party provider like BouncyCastle (BC) must be applied.

    There are two bugs in the Java / BC code:

    Both problems can be solved by replacing the lines

    ZeroBytePadding c = new ZeroBytePadding();
    PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);
    

    by

    BufferedBlockCipher pbbc = new BufferedBlockCipher(new CBCBlockCipher(rijndael));
    

    Then the ciphertexts produced by both codes are identical.

    A few additional notes: