Расшифровать mcrypt с помощью openssl

Так как mcrypt считается устаревшим, моя задача заключается в обновлении текущего кода для использования openssl. Звучит просто, но... после нескольких дней попыток и неудач я чувствую себя безумным.

Мой вопрос к вам: есть ли способ, которым вы можете расшифровать данные openssl, ранее зашифрованные с помощью mcrypt? Я прочитал столько сообщений по этому вопросу, и большинство из них говорит, что предыдущее заполнение данных вручную было/необходимо, прежде чем запускать mcrypt на нем. Проблема заключается в том, что данные mcrypt-ed уже зашифрованы (с автоматическим заполнением нулевого заполнения mcrypt) и находятся в базе данных, поэтому изменение этого невозможно и/или желательно.

Упоминания:

  • используется алгоритм rijndael-128 cbc с 32-байтным ключом (поэтому я использую aes-256-cbc для openssl).
  • Я использую оболочку openssl для php (php-crypto).
  • Мне удалось выполнить обратную операцию (декодировать openssl с помощью mcrypt), просто удалив конечные декодированные символы, если они не являются альфа-числами.
  • Вручную заполнять данные до mcrypt-ing, а затем расшифровывать их с помощью openssl работает как шарм, но это не проблема.

Некоторые фрагменты кода:

// Simple mcrypt encrypt, decrypt with php-crypto example
// This doesn't work and produces a "Finalizing of cipher failed" error
        $data = "This is a text";
        $strMcryptData=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);

        $algorithm = 'aes-256-cbc';
        $cipher = new Cipher($algorithm);
        $sim_text = $cipher->decrypt($strMcryptData, $key, $iv);

// Simple mcrypt encrypt with padding, decrypt with php-crypto
// Works and produces the correct text on decryption
        $pad =  $blocksize - (strlen($data) % $blocksize);
        $text = $data;
        $text .= str_repeat(chr($pad), $pad);
        $strPaddedData=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);

        $sim_text = $cipher->decrypt($strPaddedData, $key, $iv);

Ответ 1

Если вы зашифруете в mcrypt без добавления PKCS7 вручную, mcrypt с радостью добавит ваш открытый текст с NUL байтами.

OpenSSL будет заполнять PKCS7 для вас при использовании aes-X-cbc. Несчастливое следствие этого состоит в том, что если у вас есть AES-CBC(NULL_PADDED(plaintext)) и попробуйте его расшифровать, openssl_decrypt попытается удалить прокладку и выйти из строя.

Сравните http://3v4l.org/bdQe9 vs http://3v4l.org/jr68f и http://3v4l.org/K6ZEU

Расширение OpenSSL в настоящее время не предлагает вам сказать: "Эта строка не заполняется, пожалуйста, не удаляйте прописью для меня", а затем удалите байты NUL самостоятельно. Вы должны зашифровать с помощью дополнения PKCS7 для успешного завершения дешифрования.

Несмотря на то, что это ограничение OpenSSL, следует подчеркнуть, что единственная причина, по которой вы работаете в ней, состоит в том, что mcrypt ужасно.

Ответ 2

Немного старый, но вы можете решить это с небольшой работой. Вы можете сказать PHP OpenSSL, что зашифрованная строка не заполнена, и сообщите ей, чтобы дать вам исходный вывод (так что вам также не нужно декодировать base64). Затем вы можете отбросить нули от конца результирующей строки, если длина строки будет отлично делиться на IV (это проверка работоспособности, как если бы итоговая строка не делилась на IV, тогда она не была заполнены вообще).

Помните, что этот код имеет два основных ограничения:

  • Если в любой момент вы зашифровали законную строку, которая закончилась двумя или более байтами NULL, тогда этот код не даст вам тот же результат.

  • Если для заполнения строки понадобился только один нулевой байт, то этот код не будет разбивать его.

Вы можете решить оба из этих проблем, если знаете FACT, что вы не шифровали ничего, что заканчивается нулевыми байтами, вы можете изменить код, который разрешает нули, чтобы просто выполнить preg_replace; просто убедитесь, что вы привязываете регулярное выражение к концу строки, чтобы оно только отделялось от конца.

http://3v4l.org/kYAXn

Очевидно, что этот код не содержит большого отказа от ответственности и, пожалуйста, протестируйте его в своем прецеденте, но кто-то может надеяться найти это полезным.

Ответ 3

Не должно быть никаких существенных различий, кроме заполнения. Вы должны иметь возможность вызывать EVP_CIPHER_CTX_set_padding, если напрямую используете конструкторы OpenSSL (EVP) более высокого уровня. Я предполагаю, что аргумент заполнения должен быть равен нулю, хотя он не документирован. Для этого вам нужен предварительно сконфигурированный контекст шифрования/расшифровки.

Затем вы получите свой открытый текст такой же длины, как и зашифрованный текст. От нуля до пятнадцати байтов в конце будет установлено ноль. Вам необходимо удалить эти байты вручную. Если открытый текст заканчивается нулевыми байтами, то они также будут удалены; что, однако, никогда, если открытый текст является печатаемой строкой (которая использует 8-битную кодировку). Вы можете убедиться, что вы не удалите более 15 байт.

Если вы получаете полностью случайный текст, то ваш ключ или зашифрованный текст неверны. Если вы получаете читаемый открытый текст, но для первых 16 байтов, то ваша обработка IV неверна.