Переход с mcrypt с Blowfish и ECB на OpenSSL

В (не столь отдаленном) прошлом было принято решение (кем-то, кто здесь больше работает) всегда «шифровать» идентификаторы базы данных во что-то еще, на лету, всякий раз, когда это необходимо для внешней связи.

Теперь мы перешли с PHP 5.x на PHP 7.0 для нашего основного приложения, а наши микросервисы, разбросанные по нашей инфраструктуре, работают либо на 7.0, либо на 7.1. Серверы 7.1 продолжают выдавать предупреждения об устаревании для mcrypt. Ничего страшного, пока. Но поскольку PHP 7.2 не за горами, мы хотим продолжать обновлять его. Мкрипт блокирует.

Сохранение всех текущих зашифрованных значений в 60 таблицах в 1400 базах данных — огромная задача. Есть ли способ использовать OpenSSL с Blowfish и ECB, чтобы получить одни и те же закодированные и декодированные значения, чтобы убаюкать нас ложным чувством безопасности? Все это для того, чтобы мы могли заранее планировать миграцию нашей базы данных.

По сути, текущее зашифрованное значение таково:

item:13fb7533bf19399ff114468b194ebfaf

Это идентификатор 123. Чтобы добраться до этой строки, выполняются следующие функции:

$id   = 123;
$type = 'item';

$serialized = serialize('' . $id); // To make sure always a string gets put in

$ivSize = mcrypt_create_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB), MCRYPT_RAND);
$iv     = mcrypt_create_iv($ivSize);

$passCrypt = mcrypt_encrypt(MCRYPT_BLOWFISH, $type, $serialized, MCRYPT_MODE_ECB, $iv);
$encoded   = bin2hex($passCrypt); // `13fb7533bf19399ff114468b194ebfaf`

$encryptedId = $type . ':' . $encoded;

Это дает окончательный результат item:13fb7533bf19399ff114468b194ebfaf.

Теперь наоборот:

$encryptedId = 'item:13fb7533bf19399ff114468b194ebfaf';

$type = 'item';
$encryptedIdOnly = substr($encryptedId, strlen($type) + 1); // `13fb...`

$decoded   = hex2bin($encryptedIdOnly);
$iv        = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB), MCRYPT_RAND);
$decrypted = mcrypt_decrypt(MCRYPT_BLOWFISH, 'item', $decoded, MCRYPT_MODE_ECB, $iv); // This gives ' `s:3:"123";` '

$unserialized = unserialize($decrypted); // '123'

Я пробовал это в течение нескольких часов, но я совершенно ослеплен любой криптовалютой (но я хочу научиться!). Мой текущий код:

$cipher = 'BF-ECB';
//$cipher = 'BF'; (I've tried both, no difference)

$isCtypeXDigit = ctype_xdigit($decipher);
$decoded       = hex2bin($decipher);
$ivLength      = openssl_cipher_iv_length($cipher);
$randomBytes   = openssl_random_pseudo_bytes($ivLength);
$decrypted     = openssl_decrypt($decoded, $cipher, $prefix, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $randomBytes);
$unserialized  = unserialize($decrypted);

Что дает мне тысячу вещей, похожих на ��IY_Lc�d:�_���. Может ли кто-нибудь пролить свет на это - возможно ли это вообще?


person Mave    schedule 08.03.2018    source источник


Ответы (1)


Это очень сложно. Вы можете просто использовать код.

# cat a.php
<?php
function mcrypt_blowfish_encrypt_hex($key, $str)
{
    $encrypted = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $str, MCRYPT_MODE_ECB);
    return bin2hex($encrypted);
}

function make_openssl_blowfish_key($key)
{
    if("$key" === '')
        return $key;

    $len = (16+2) * 4;
    while(strlen($key) < $len) {
        $key .= $key;
    }
    $key = substr($key, 0, $len);
    return $key;
}

function openssl_blowfish_encrypt_hex($key, $str)
{
    $blockSize = 8;
    $len = strlen($str);
    $paddingLen = intval(($len + $blockSize - 1) / $blockSize) * $blockSize - $len;
    $padding = str_repeat("\0", $paddingLen);
    $data = $str . $padding;
    $key = make_openssl_blowfish_key($key);
    $encrypted = openssl_encrypt($data, 'BF-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    return bin2hex($encrypted);
}

function openssl_blowfish_decrypt_hex($key, $hex)
{
    $key = make_openssl_blowfish_key($key);
    $decrypted = openssl_decrypt(hex2bin($hex), 'BF-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    return rtrim($decrypted, "\0");
}


function test()
{
    for($i = 1; $i < 32; $i++) {
        for($j = 1; $j < 32; $j++) {
            $key = str_repeat('' . rand(0, 9), $j);
            $str = str_repeat('' . rand(0, 9), $i);

            $encoded_openssl = openssl_blowfish_encrypt_hex($key, $str);
            $decoded_openssl = openssl_blowfish_decrypt_hex($key, $encoded_openssl);
            if($decoded_openssl != $str)
                die("encrypt($key, $str) wrong: $encoded_openssl: decrypt failed\n");


            if(function_exists('mcrypt_encrypt')) {
                $encoded_mcrypt = mcrypt_blowfish_encrypt_hex($key, $str);
                if($encoded_openssl != $encoded_mcrypt)
                    die("encrypt($key, $str) wrong: $encoded_openssl, mcrypt=$encoded_mcrypt\n");
            }

            echo "key='$key', str='$str', encrypted='$encoded_openssl'\n";
        }
    }
}

echo "openssl: thisismyitemyes:" . openssl_blowfish_encrypt_hex('thisismyitemyes', serialize('6918')) . "\n";
echo "openssl: headphone:" . openssl_blowfish_encrypt_hex('headphone', serialize('581856')) . "\n";

test();

И запустите, это работает:

# php a.php
openssl: thisismyitemyes:b192ac0f6105416a710aec3ce92b1085
openssl: headphone:ef057c036eb024865406838c62590a93
key='7', str='3', encrypted='945b638624ecbd5e'
key='22', str='1', encrypted='3daf096bdc744d8a'
key='888', str='0', encrypted='b164bb0b603f439e'
key='2222', str='9', encrypted='d3458df30aef0b4b'
...
...
key='3333333333333333333333333333333', str='11111111111111111111111111111', encrypted='b0c9bf45d6f5c7b3b0c9bf45d6f5c7b3b0c9bf45d6f5c7b363a25777c712f1d5'
key='4444444444444444444444444444444', str='999999999999999999999999999999', encrypted='dd6aaf466121c0f6dd6aaf466121c0f6dd6aaf466121c0f659a2271369ab6731'
key='7777777777777777777777777777777', str='3333333333333333333333333333333', encrypted='6591e9cc92a6473a6591e9cc92a6473a6591e9cc92a6473a208a7a562babc60c'

Проблемы:

  1. IV игнорируются в режиме ECB, поэтому просто удалите все IV в своем коде.

  2. Из-за ошибки: https://bugs.php.net/bug.php?id=72362 . В mcrypt клавиша Blowfish заменяется короткой клавишей. Но в openssl клавиша Blowfish дополняется нулями с помощью короткой клавиши. Поэтому нам нужно сделать зацикленный ключ для openssl, чтобы расшифровать шифрование mcrypt.

  3. Когда вы используете заполнение нулями с openssl (сохраняйте тот же вывод mcrypt), вы должны выполнять заполнение самостоятельно. Что ж, я проделал трюк, чтобы получить paddingLen, но это очень просто: просто подумайте, сколько байтов мы должны добавить, чтобы общая длина составила 0/8/16/24/32/40 и т. д.

person shawn    schedule 08.03.2018
comment
@shawn Я думаю, вам следует обрезать только \0 в openssl_blowfish_decrypt_hex, чтобы не удалять пробелы и 0. Я думаю, что последняя строка должна выглядеть как return trim($decrypted, "\0"); - person mleko; 29.05.2019