Hướng dẫn aes/cbc/pkcs5padding php - aes / cbc / pkcs5padding php

I must match an encrypted JSON object from Java to PHP but I failed to do so. Tried almost all solutions on stackoverflow, still no luck.

DOC says field must be obtained through AES encryption of the JSON representation of all the fields the merchant wants to send. Encryption algorithm must be AES/CBC/PKCS5Padding and must use as encrypting key the one provided. The initialization vector to be used for data encryption must be 16 bytes length equal to 0. Encrypted byte array must encoded to base64.

{"addrMatch":"N"} should convert to q8zzcOYHKggpSFXdmdZdObe2R/1EWnFgco2FJzjhtJQ=

I have the following code:

import java.security.InvalidAlgorithmParameterException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
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;
import javax.xml.bind.DatatypeConverter;

public class Utility {
    public static String encode3DSdata(String APISecretMerchant, String JSONobject) throws Throwable {

        // Initialization vector
        byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        // AES Key from the API merchant key
        byte[] key = APISecretMerchant.substring(0, 16).getBytes();
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

        byte[] toEncrypt = JSONobject.getBytes("UTF-8");
        // Encrypt
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        byte[] encrypted = cipher.doFinal(toEncrypt);
        // Convert to base64
        return DatatypeConverter.printBase64Binary(encrypted);
    }
}

Here is my code on PHP without luck:

        function pkcs5pad($text,$blocksize) {
            $pad = $blocksize - (strlen($text) % $blocksize);
            return $text.str_repeat(chr($pad), $pad);
        }

        function encrypt($data, $key)
        {
            $json = iconv('utf-8', 'utf-8//IGNORE', json_encode($data,JSON_UNESCAPED_SLASHES));
            $padded=pkcs5pad($json,16);
            $encrypted = openssl_encrypt($padded, 'AES-128-CBC', $key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, self::iv);
            return base64_encode($encrypted);
        }

        const iv = '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'; // also tried as "0000000000000000"
        $key='c-MCjRx4X-pD-Lux';
        $data=['addrMatch'=>'N'];
        echo encrypt($data,$key);

I produce : Bpo0neCnY+dFrdISLYcefU8sqhX/4HSr5Io+zUe4sro=

Any help would be appreciated.

import android.support.annotation.Nullable; import android.util.Base64; import java.nio.ByteBuffer; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * AesCipher *

Encode/Decode text by password using AES-128-CBC algorithm

*/ public class AesCipher { public static final int INIT_VECTOR_LENGTH = 16; /** * @see how-to-convert-a-byte-array-to-a-hex-string */ private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); /** * Encoded/Decoded data */ protected String data; /** * Initialization vector value */ protected String initVector; /** * Error message if operation failed */ protected String errorMessage; private AesCipher() { super(); } /** * AesCipher constructor. * * @param initVector Initialization vector value * @param data Encoded/Decoded data * @param errorMessage Error message if operation failed */ private AesCipher(@Nullable String initVector, @Nullable String data, @Nullable String errorMessage) { super(); this.initVector = initVector; this.data = data; this.errorMessage = errorMessage; } /** * Encrypt input text by AES-128-CBC algorithm * * @param secretKey 16/24/32 -characters secret password * @param plainText Text for encryption * @return Encoded string or NULL if error */ public static AesCipher encrypt(String secretKey, String plainText) { String initVector = null; try { // Check secret length if (!isKeyLengthValid(secretKey)) { throw new Exception("Secret key's length must be 128, 192 or 256 bits"); } // Get random initialization vector SecureRandom secureRandom = new SecureRandom(); byte[] initVectorBytes = new byte[INIT_VECTOR_LENGTH / 2]; secureRandom.nextBytes(initVectorBytes); initVector = bytesToHex(initVectorBytes); initVectorBytes = initVector.getBytes("UTF-8"); IvParameterSpec ivParameterSpec = new IvParameterSpec(initVectorBytes); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec); // Encrypt input text byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8")); ByteBuffer byteBuffer = ByteBuffer.allocate(initVectorBytes.length + encrypted.length); byteBuffer.put(initVectorBytes); byteBuffer.put(encrypted); // Result is base64-encoded string: initVector + encrypted result String result = Base64.encodeToString(byteBuffer.array(), Base64.DEFAULT); // Return successful encoded object return new AesCipher(initVector, result, null); } catch (Throwable t) { t.printStackTrace(); // Operation failed return new AesCipher(initVector, null, t.getMessage()); } } /** * Decrypt encoded text by AES-128-CBC algorithm * * @param secretKey 16/24/32 -characters secret password * @param cipherText Encrypted text * @return Self object instance with data or error message */ public static AesCipher decrypt(String secretKey, String cipherText) { try { // Check secret length if (!isKeyLengthValid(secretKey)) { throw new Exception("Secret key's length must be 128, 192 or 256 bits"); } // Get raw encoded data byte[] encrypted = Base64.decode(cipherText, Base64.DEFAULT); // Slice initialization vector IvParameterSpec ivParameterSpec = new IvParameterSpec(encrypted, 0, INIT_VECTOR_LENGTH); // Set secret password SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); // Trying to get decrypted text String result = new String(cipher.doFinal(encrypted, INIT_VECTOR_LENGTH, encrypted.length - INIT_VECTOR_LENGTH)); // Return successful decoded object return new AesCipher(bytesToHex(ivParameterSpec.getIV()), result, null); } catch (Throwable t) { t.printStackTrace(); // Operation failed return new AesCipher(null, null, t.getMessage()); } } /** * Check that secret password length is valid * * @param key 16/24/32 -characters secret password * @return TRUE if valid, FALSE otherwise */ public static boolean isKeyLengthValid(String key) { return key.length() == 16 || key.length() == 24 || key.length() == 32; } /** * Convert Bytes to HEX * * @param bytes Bytes array * @return String with bytes values */ public static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } /** * Get encoded/decoded data */ public String getData() { return data; } /** * Get initialization vector value */ public String getInitVector() { return initVector; } /** * Get error message */ public String getErrorMessage() { return errorMessage; } /** * Check that operation failed * * @return TRUE if failed, FALSE otherwise */ public boolean hasError() { return this.errorMessage != null; } /** * To string return resulting data * * @return Encoded/decoded data */ public String toString() { return getData(); } } // USAGE String secretKey = "26kozQaKwRuNJ24t"; String text = "Some text"; AesCipher encrypted = AesCipher.encrypt(secretKey, text); AesCipher decrypted = AesCipher.decrypt(secretKey, encrypted); encrypted.hasError(); // TRUE if operation failed, FALSE otherwise encrypted.getData(); // Encoded/Decoded result encrypted.getInitVector(); // Get used (random if encode) init vector // decrypted.* has identical methods