3

Hi,

I'm trying to migrate my APP from Yii2 (PHP 7.4) to Laravel 12 (PHP 8.4) and I also want to move from MySQL 5.7 to Postgres.

In the old app I used to encrypt PII data and saved it to the database like this:

$data = \Yii::$app->security->encryptByPassword(serialize($pii), env('PII_ENCRYPTION_PASSWORD'));
$this->data['pii'] = bin2hex($data);

$data is a json column in MySQL 5.7.

Now I want to decrypt the data, because I want to extract some of them for statistics. In my Yii2 app I do it like this:

$pii = unserialize(Yii::$app->security->decryptByPassword(hex2bin($data), env('PII_ENCRYPTION_PASSWORD')));

In laravel I tried to copy all relavant code from \yii\base\Security::decryptByPassword and I ensured via debugging settings like $keySize. Because it didn't work I found that the output from hex2bin is different from one app to another.

Is that possible for different PHP versions? Do I have invisible charactores in my yii2 code?

My current code looks like this:

class Yii2Decryptor
{
    protected string $cipher = 'AES-256-CBC';

    protected string $kdfHash = 'sha256';

    protected string $macHash = 'sha256';

    protected int $derivationIterations = 100000;

    protected string $authKeyInfo = 'AuthorizationKey';

    protected int $keySize = 16;

    protected int $blockSize = 16;

    public function decrypt(string $hexData, string $password): ?string
    {
        $data = hex2bin($hexData);

        $keySalt = $this->byteSubstr($data, 0, $this->keySize);

        $key = hash_pbkdf2($this->kdfHash, $password, $keySalt, $this->derivationIterations, $this->keySize, true);

        $authKey = hash_hkdf($this->kdfHash, $key, $this->keySize, $this->authKeyInfo, $keySalt);
        dump($hexData, $data, $keySalt, $key, $authKey); // $data is already different. Next line $data is `false`
        $data = $this->validateData($this->byteSubstr($data, $this->keySize), $authKey);
        if ($data === false) {
            return null;
        }

        $iv = $this->byteSubstr($data, 0, $this->blockSize);
        $encrypted = $this->byteSubstr($data, $this->blockSize);

        $decrypted = openssl_decrypt($encrypted, $this->cipher, $key, OPENSSL_RAW_DATA, $iv);

        return $decrypted === false ? null : $decrypted;
    }

    private function byteSubstr(string $data, int $start, ?int $length = null): string
    {
        if ($length === null) {
            $length = $this->byteLength($data);
        }

        return mb_substr($data, $start, $length, '8bit');
    }

    private function byteLength(string $data): int
    {
        return mb_strlen($data, '8bit');
    }

    protected function validateData(string $data, string $key): false|string
    {
        $test = hash_hmac($this->macHash, '', '');
        $hasLength = $this->byteLength($test);
        if ($this->byteLength($data) >= $hasLength) {
            $hash = $this->byteSubstr($data, 0, $hasLength);
            $pureData = $this->byteSubstr($data, $hasLength);
            $calculatedHash = hash_hmac($this->macHash, $pureData, $key);
            if (hash_equals($hash, $calculatedHash)) {
                return $pureData;
            }
        }

        return false;
    }

}

The fact that already the output from hex2bin is different is driving me nuts...

In my database the encrypted string looks like this:

2b0035bed2f044836ed030acc7cdf699663262376262363931346637636436316439383166613264616637346332323330366564373466396439363765303032306161333839666433373565313036328eca8d3c54b05cbc57eac76be1235e61c0a5e616eb889ece1dcb26ec926e09abcdae94c0ebef8da2a4fc544e78c23536704622e8546e68c9fa90965e30dd9a0342f8754a2ef3b737ba74f6520054958888abb84fc57ddd460a5d41e33e11ccb1859b84f32e132478c0308863f667103e334031046872e98c6b4a7d332e1511387580f86e30ead0483f1b76aa6ec18a0a0c35b04050d6c02e716761be86e967753a267c7e506cee8e789d58a63946de56fbf99d7ef860a35dd9526a7c0034269243619e6852adb58886deec4f8493a82982032504dca0470a863023faecaf928aad0888f8c98c599a463297944e5d01c3235c22e1ce22876d9775835bc99a96a21e0312ec0a72d8acd83591ae22ac5bb269635a0071af279b98d8b4faf9a164d60cccc97411899b4c91fedcc1c4752918cefeb2a7e7d44c1b5f3b419b2ad8fe2e979d9af0406c7607872b37e5fecb529ca09de6907e117742322e22bdb91919bf8e3a2ac9748d334a70119b14f7c4c4fc8815e54d881c32ff5d8b64a69cb72f06844a47b314ef90fb602c3e31988e9abe5db8c29e1f06cec2c27d65de4243649883f3c8b0d0d6360d8beb15345d355a6e2fcd8cdad64ee338acd724d47cf0b75d9ad0b767b929b3dd3cf1d8c37af50fa646be07ccbba9db4cf74d31dda080876aa05d8435d00f1e6d01a419b70b34cf5e
7
  • hex2bin should yield the same output. What does the input data look like for the same password on both systems? Commented Nov 14 at 0:04
  • I've updated the question and added example data. When I do a hex2bin on this string, wether it's copy pasted or fetched from databse, hex2bin has a different outcome. Or is t possible, that there are some non printable characters? Commented Nov 14 at 7:20
  • You have working Yii2 code to decrypt the strings you have in the database. One solution would be to use that, to get the decrypted data, and then to re-encrypt it with Laravel or just basic PHP. Commented Nov 14 at 8:18
  • 1
    "hex2bin has a different outcome" I doubt it. Commented Nov 14 at 8:27
  • 1
    unserialize() may have different configuration, YII may have different configuration - but hex2bin() normally stays the same regardless which PHP configuration. The confusion most likely stems from mixing two code-bases that both work as ubiquitous app frameworks (= "bad idea" #1) and then you even add more overhead by changing the database server even while having a problem migrating the data already (= "bad idea" #2). Better is to do the most important step first, like decrypting the data and dump the original data unencrypted for archival. Commented Nov 14 at 8:51

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.