0

I have got the Java Code from Payment Gateway for encryption of data. But My system is in PHP and I am trying to encrypt the same data in PHP. But I am getting different result. I tried all the help available but didn't got solution.

Encrypted Result can be tested by entering the encdata in https://mirror.kbzbank.com/B001/directpay.html

I believe I am missing some steps and I am not able to find what's wrong in my PHP code. Any suggestion or help would be grateful.

Java Code:

import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AesCipher {
    public static final int INIT_VECTOR_LENGTH = 16;
    /**
     * @see <a href="https://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to-a-hex-string-in-java">how-to-convert-a-byte-array-to-a-hex-string</a>
     */
    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( String initVector,  String data, 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");
            }
            
            byte[] l_encrypted = null;
            Cipher l_encrcipher = null;

            final byte[] keyBytes = new byte[16];
            byte[] pwdBytes = null;

            pwdBytes = secretKey.getBytes("UTF-8");
            
            System.arraycopy(pwdBytes, 0, keyBytes, 0, pwdBytes.length);
            
            l_encrcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES");
            final IvParameterSpec ivParamSpec = new IvParameterSpec(keyBytes);
            l_encrcipher.init(1, skeySpec, ivParamSpec);
            l_encrypted = l_encrcipher.doFinal(plainText.getBytes());
            
            String result = Base64.getEncoder().encodeToString(l_encrypted);
            result =  result.replaceAll("\\r\\n","");

            // Return successful encoded object
            return new AesCipher(initVector, result, null);
            
        } catch (Throwable t) {
            t.printStackTrace();
            // Operation failed
            return new AesCipher(initVector, 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() == 6 || 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();
    }
    
    
    public static void main(String[] args) {
        // USAGE
        String secretKey = "S%Y#N@";
        String text = "fldClientCode=101|fldMerchCode=EPI0008|fldTxnCurr=MMK|fldTxnAmt=1000|fldTxnScAmt=0|fldMerchRefNbr=ACBS12345|fldSucStatFlg=N|fldFailStatFlg=N|fldDatTimeTxn=07/03/202213:01:01|checkSum=f0b04f0915619669987de294e4c1931d";

        AesCipher encrypted = AesCipher.encrypt(secretKey, text);
        System.out.println(encrypted.getData());

    }

}

PHP Code I am trying :

<?php

/**
 * AesCipher
 *
 * Encode/Decode text by password using AES-128-CBC algorithm
 */
class AesCipher
{
    const CIPHER = 'AES-128-CBC';
    const INIT_VECTOR_LENGTH = 16;

    /**
     * Encoded/Decoded data
     *
     * @var null|string
     */
    protected $data;
    /**
     * Initialization vector value
     *
     * @var string
     */
    protected $initVector;
    /**
     * Error message if operation failed
     *
     * @var null|string
     */
    protected $errorMessage;

    /**
     * AesCipher constructor.
     *
     * @param string $initVector        Initialization vector value
     * @param string|null $data         Encoded/Decoded data
     * @param string|null $errorMessage Error message if operation failed
     */
    public function __construct($initVector, $data = null, $errorMessage = null)
    {
        $this->initVector = $initVector;
        $this->data = $data;
        $this->errorMessage = $errorMessage;
    }

    /**
     * Encrypt input text by AES-128-CBC algorithm
     *
     * @param string $secretKey 16/24/32 -characters secret password
     * @param string $plainText Text for encryption
     *
     * @return self Self object instance with data or error message
     */
    public static function encrypt($secretKey, $plainText)
    {
        try {
            // Check secret length
            if (!static::isKeyLengthValid($secretKey)) {
                throw new \InvalidArgumentException("Secret key's length must be 128, 192 or 256 bits");
            }

            // Get random initialization vector
            $initVector = bin2hex(openssl_random_pseudo_bytes(static::INIT_VECTOR_LENGTH / 2));

            // Encrypt input text
            $raw = openssl_encrypt(
                $plainText,
                static::CIPHER,
                $secretKey,
                OPENSSL_RAW_DATA,
                $initVector
            );

            // Return base64-encoded string: initVector + encrypted result
            $result = base64_encode($initVector . $raw);

            if ($result === false) {
                // Operation failed
                return new static($initVector, null, openssl_error_string());
            }

            // Return successful encoded object
            return new static($initVector, $result);
        } catch (\Exception $e) {
            // Operation failed
            return new static(isset($initVector), null, $e->getMessage());
        }
    }

    /**
     * Decrypt encoded text by AES-128-CBC algorithm
     *
     * @param string $secretKey  16/24/32 -characters secret password
     * @param string $cipherText Encrypted text
     *
     * @return self Self object instance with data or error message
     */
    public static function decrypt($secretKey, $cipherText)
    {
        try {
            // Check secret length
            if (!static::isKeyLengthValid($secretKey)) {
                throw new \InvalidArgumentException("Secret key's length must be 128, 192 or 256 bits");
            }

            // Get raw encoded data
            $encoded = base64_decode($cipherText);
            // Slice initialization vector
            $initVector = substr($encoded, 0, static::INIT_VECTOR_LENGTH);
            // Slice encoded data
            $data = substr($encoded, static::INIT_VECTOR_LENGTH);

            // Trying to get decrypted text
            $decoded = openssl_decrypt(
                $data,
                static::CIPHER,
                $secretKey,
                OPENSSL_RAW_DATA,
                $initVector
            );

            if ($decoded === false) {
                // Operation failed
                return new static(isset($initVector), null, openssl_error_string());
            }

            // Return successful decoded object
            return new static($initVector, $decoded);
        } catch (\Exception $e) {
            // Operation failed
            return new static(isset($initVector), null, $e->getMessage());
        }
    }

    /**
     * Check that secret password length is valid
     *
     * @param string $secretKey 16/24/32 -characters secret password
     *
     * @return bool
     */
    public static function isKeyLengthValid($secretKey)
    {
        $length = strlen($secretKey);

        return $length == 6 || $length == 16 || $length == 24 || $length == 32;
    }

    /**
     * Get encoded/decoded data
     *
     * @return string|null
     */
    public function getData()
    {
        return $this->data;
    }

    /**
     * Get initialization vector value
     *
     * @return string|null
     */
    public function getInitVector()
    {
        return $this->initVector;
    }

    /**
     * Get error message
     *
     * @return string|null
     */
    public function getErrorMessage()
    {
        return $this->errorMessage;
    }

    /**
     * Check that operation failed
     *
     * @return bool
     */
    public function hasError()
    {
        return $this->errorMessage !== null;
    }

    /**
     * To string return resulting data
     *
     * @return null|string
     */
    public function __toString()
    {
        return $this->getData();
    }
}

// USAGE

$secretKey = 'S%Y#N@';
$text = "fldClientCode=101|fldMerchCode=EPI0008|fldTxnCurr=MMK|fldTxnAmt=1000|fldTxnScAmt=0|fldMerchRefNbr=ACBS12345|fldSucStatFlg=N|fldFailStatFlg=N|fldDatTimeTxn=07/03/202213:01:01|checkSum=f0b04f0915619669987de294e4c1931d";

$encrypted = AesCipher::encrypt($secretKey, $text);
print_r($encrypted);
$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
  • 2
    For the result to be identical, the PHP code in `encrypt()` must use the key as IV and pad it to 16 bytes with 0x00 values if necessary: `$initVector = str_pad($secretKey, 16, "\0")` and no concatenation of IV and ciphertext must be done: `$result = base64_encode($raw)`. Then `print($encrypted->getData())` returns the result of the Java code. `decrypt()` must be adapted to `encrypt()`. Both codes are insecure, the Java code even buggy (`encrypted.getInitVector()` returns `null`). Most of this has already been described in the comments to your last question (meanwhile deleted by you) . – Topaco Mar 11 '22 at 07:49
  • Please do never use a String/password as AES key. This is heavily insecure. At least use a password based key generator like PBKDF2. – Robert Mar 11 '22 at 18:47

0 Answers0