PHP AES шифрует / дешифрует - PullRequest
46 голосов
/ 06 августа 2010

Я нашел пример для en / decoding строк в PHP.Сначала это выглядит очень хорошо, но не работает: - (

Кто-нибудь знает, в чем проблема?

$Pass = "Passwort";
$Clear = "Klartext";

$crypted = fnEncrypt($Clear, $Pass);
echo "Encrypted: ".$crypted."</br>";

$newClear = fnDecrypt($crypted, $Pass);
echo "Decrypted: ".$newClear."</br>";

function fnEncrypt($sValue, $sSecretKey) {
    return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, $sDecrypted, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
}

function fnDecrypt($sValue, $sSecretKey) {
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sEncrypted), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}

Результат:

Зашифровано: boKRNTYYNp7AiOvY1CidqsAn9wX4ufz/D9XrpjAOPk8=

Расшифровано: —‚(ÑÁ ^ yË~F'¸®Ó–í œð2Á_B‰Â—

Ответы [ 8 ]

75 голосов
/ 11 мая 2015

Пожалуйста, используйте существующую безопасную библиотеку шифрования PHP

Как правило, писать собственную криптографию - плохая идея, если у вас нет опыта взлома криптографических реализаций других людей.

Ни один из приведенных здесь примеров не проверяет подлинность зашифрованного текста , что делает его уязвимым для атак переписывания битов.

Если вы можете установить расширения PECL, libsodium еще лучше

<?php
// PECL libsodium 0.2.1 and newer

/**
 * Encrypt a message
 * 
 * @param string $message - message to encrypt
 * @param string $key - encryption key
 * @return string
 */
function safeEncrypt($message, $key)
{
    $nonce = \Sodium\randombytes_buf(
        \Sodium\CRYPTO_SECRETBOX_NONCEBYTES
    );

    return base64_encode(
        $nonce.
        \Sodium\crypto_secretbox(
            $message,
            $nonce,
            $key
        )
    );
}

/**
 * Decrypt a message
 * 
 * @param string $encrypted - message encrypted with safeEncrypt()
 * @param string $key - encryption key
 * @return string
 */
function safeDecrypt($encrypted, $key)
{   
    $decoded = base64_decode($encrypted);
    $nonce = mb_substr($decoded, 0, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
    $ciphertext = mb_substr($decoded, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');

    return \Sodium\crypto_secretbox_open(
        $ciphertext,
        $nonce,
        $key
    );
}    

Затем, чтобы проверить это:

<?php
// This refers to the previous code block.
require "safeCrypto.php"; 

// Do this once then store it somehow:
$key = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_KEYBYTES);
$message = 'We are all living in a yellow submarine';

$ciphertext = safeEncrypt($message, $key);
$plaintext = safeDecrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Это может использоваться в любой ситуации, когда вы передаете данные клиенту (например, зашифрованные куки-файлы для сеансов без серверного хранилища, зашифрованные параметры URL и т. Д.) С достаточно высокой степенью уверенности, что конечный пользователь не сможет расшифровать или надежно подделать его.

Поскольку libsodium является кроссплатформенным , это также упрощает связь с PHP, например, с. Java-апплеты или собственные мобильные приложения.


Примечание. Если вам необходимо добавить зашифрованные файлы cookie на базе libsodium в ваше приложение, мой работодатель Paragon Initiative Enterprises разрабатывает библиотеку под названием Halite , которая сделает все это за вас. .

52 голосов
/ 06 августа 2010

$sDecrypted и $sEncrypted не определены в вашем коде.Посмотрите решение, которое работает (, но не является безопасным! ):


STOP!

Этот пример небезопасен! Не используйте его!


$Pass = "Passwort";
$Clear = "Klartext";        

$crypted = fnEncrypt($Clear, $Pass);
echo "Encrypred: ".$crypted."</br>";

$newClear = fnDecrypt($crypted, $Pass);
echo "Decrypred: ".$newClear."</br>";        

function fnEncrypt($sValue, $sSecretKey)
{
    return rtrim(
        base64_encode(
            mcrypt_encrypt(
                MCRYPT_RIJNDAEL_256,
                $sSecretKey, $sValue, 
                MCRYPT_MODE_ECB, 
                mcrypt_create_iv(
                    mcrypt_get_iv_size(
                        MCRYPT_RIJNDAEL_256, 
                        MCRYPT_MODE_ECB
                    ), 
                    MCRYPT_RAND)
                )
            ), "\0"
        );
}

function fnDecrypt($sValue, $sSecretKey)
{
    return rtrim(
        mcrypt_decrypt(
            MCRYPT_RIJNDAEL_256, 
            $sSecretKey, 
            base64_decode($sValue), 
            MCRYPT_MODE_ECB,
            mcrypt_create_iv(
                mcrypt_get_iv_size(
                    MCRYPT_RIJNDAEL_256,
                    MCRYPT_MODE_ECB
                ), 
                MCRYPT_RAND
            )
        ), "\0"
    );
}

Но есть и другие проблемы в этом коде, которые делают его небезопасным, в частности, использование ECB (который не является режим шифрования , только строительный блок, поверх которого можно определить режимы шифрования).См. Ответ Fab Sa для быстрого решения наихудших проблем и Ответ Скотта о том, как сделать это правильно.

33 голосов
/ 22 октября 2017

Если вы не хотите использовать сильную зависимость для чего-то решаемого в 15 строках кода, используйте встроенные функции OpenSSL . Большинство установок PHP поставляются с OpenSSL, который обеспечивает быстрое, совместимое и безопасное шифрование AES в PHP. Что ж, это безопасно, если вы следуете лучшим рекомендациям.

Следующий код:

  • использует AES256 в режиме CBC
  • совместим с другими реализациями AES, но не mcrypt , поскольку mcrypt использует PKCS # 5 вместо PKCS # 7.
  • генерирует ключ из предоставленного пароля, используя SHA256
  • генерирует hmac-хэш зашифрованных данных для проверки целостности
  • генерирует случайный IV для каждого сообщения
  • добавляет IV (16 байтов) и хеш (32 байта) к зашифрованному тексту
  • должно быть довольно безопасно

IV является общедоступной информацией и должна быть случайной для каждого сообщения. Хеш гарантирует, что данные не были подделаны.

function encrypt($plaintext, $password) {
    $method = "AES-256-CBC";
    $key = hash('sha256', $password, true);
    $iv = openssl_random_pseudo_bytes(16);

    $ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
    $hash = hash_hmac('sha256', $ciphertext, $key, true);

    return $iv . $hash . $ciphertext;
}

function decrypt($ivHashCiphertext, $password) {
    $method = "AES-256-CBC";
    $iv = substr($ivHashCiphertext, 0, 16);
    $hash = substr($ivHashCiphertext, 16, 32);
    $ciphertext = substr($ivHashCiphertext, 48);
    $key = hash('sha256', $password, true);

    if (hash_hmac('sha256', $ciphertext, $key, true) !== $hash) return null;

    return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
}

Использование:

$encrypted = encrypt('Plaintext string.', 'password'); // this yields a binary string

echo decrypt($encrypted, 'password');
// decrypt($encrypted, 'wrong password') === null
26 голосов
/ 09 сентября 2012

Для информации MCRYPT_MODE_ECB не использует IV (вектор инициализации).Режим ECB разделяет ваше сообщение на блоки, и каждый блок шифруется отдельно.Я действительно не рекомендую .

В режиме CBC используйте IV, чтобы сделать каждое сообщение уникальным.Рекомендуется использовать CBC, и его следует использовать вместо ECB.

Пример:

<?php
$password = "myPassword_!";
$messageClear = "Secret message";

// 32 byte binary blob
$aes256Key = hash("SHA256", $password, true);

// for good entropy (for MCRYPT_RAND)
srand((double) microtime() * 1000000);
// generate random iv
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), MCRYPT_RAND);


$crypted = fnEncrypt($messageClear, $aes256Key);

$newClear = fnDecrypt($crypted, $aes256Key);

echo
"IV:        <code>".$iv."</code><br/>".
"Encrypred: <code>".$crypted."</code><br/>".
"Decrypred: <code>".$newClear."</code><br/>";

function fnEncrypt($sValue, $sSecretKey) {
    global $iv;
    return rtrim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, $sValue, MCRYPT_MODE_CBC, $iv)), "\0\3");
}

function fnDecrypt($sValue, $sSecretKey) {
    global $iv;
    return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sValue), MCRYPT_MODE_CBC, $iv), "\0\3");
}

Вам необходимо запастись IV для декодирования каждого сообщения (IV * не secret),Каждое сообщение уникально, потому что каждое сообщение имеет уникальный IV.

3 голосов
/ 13 июня 2015

Несколько важных моментов, которые следует отметить при шифровании AES:

  1. Никогда не используйте простой текст в качестве ключа шифрования.Всегда хэшируйте простой текстовый ключ, а затем используйте его для шифрования.
  2. Всегда используйте Random IV (вектор инициализации) для шифрования и дешифрования. Истинная рандомизация важна.
  3. Как упоминалось выше, не используйте режим , вместо него используйте CBC.
1 голос
/ 28 августа 2012

Если вы используете MCRYPT_RIJNDAEL_128, попробуйте rtrim($output, "\0\3"). Если длина строки меньше 16, функция расшифровки вернет строку длиной 16 символов, добавив в конце 03.

Вы можете легко проверить это, например, пытаясь:

$string = "TheString";
$decrypted_string = decrypt_function($stirng, $key);

echo bin2hex($decrypted_string)."=".bin2hex("TheString");
0 голосов
/ 02 июля 2019

Это рабочее решение AES encryption - реализовано с использованием openssl.Он использует режим цепочки блоков шифрования (CBC-Mode).Таким образом, наряду с data и key можно указать iv и block size

 <?php
      class AESEncryption {

            protected $key;
            protected $data;
            protected $method;
            protected $iv;

            /**
             * Available OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
             *
             * @var type $options
             */
            protected $options = 0;

            /**
             * 
             * @param type $data
             * @param type $key
             * @param type $iv
             * @param type $blockSize
             * @param type $mode
             */
            public function __construct($data = null, $key = null, $iv = null, $blockSize = null, $mode = 'CBC') {
                $this->setData($data);
                $this->setKey($key);
                $this->setInitializationVector($iv);
                $this->setMethod($blockSize, $mode);
            }

            /**
             * 
             * @param type $data
             */
            public function setData($data) {
                $this->data = $data;
            }

            /**
             * 
             * @param type $key
             */
            public function setKey($key) {
                $this->key = $key;
            }

            /**
             * CBC 128 192 256 
              CBC-HMAC-SHA1 128 256
              CBC-HMAC-SHA256 128 256
              CFB 128 192 256
              CFB1 128 192 256
              CFB8 128 192 256
              CTR 128 192 256
              ECB 128 192 256
              OFB 128 192 256
              XTS 128 256
             * @param type $blockSize
             * @param type $mode
             */
            public function setMethod($blockSize, $mode = 'CBC') {
                if($blockSize==192 && in_array('', array('CBC-HMAC-SHA1','CBC-HMAC-SHA256','XTS'))){
                    $this->method=null;
                    throw new Exception('Invalid block size and mode combination!');
                }
                $this->method = 'AES-' . $blockSize . '-' . $mode;
            }

            /**
             * 
             * @param type $data
             */
            public function setInitializationVector($iv) {
                $this->iv = $iv;
            }

            /**
             * 
             * @return boolean
             */
            public function validateParams() {
                if ($this->data != null &&
                        $this->method != null ) {
                    return true;
                } else {
                    return FALSE;
                }
            }

            //it must be the same when you encrypt and decrypt
            protected function getIV() { 
                return $this->iv;
            }

             /**
             * @return type
             * @throws Exception
             */
            public function encrypt() {
                if ($this->validateParams()) { 
                    return trim(openssl_encrypt($this->data, $this->method, $this->key, $this->options,$this->getIV()));
                } else {
                    throw new Exception('Invalid params!');
                }
            }

            /**
             * 
             * @return type
             * @throws Exception
             */
            public function decrypt() {
                if ($this->validateParams()) {
                   $ret=openssl_decrypt($this->data, $this->method, $this->key, $this->options,$this->getIV());

                   return   trim($ret); 
                } else {
                    throw new Exception('Invalid params!');
                }
            }

        }

Пример использования:

<?php
        $data = json_encode(['first_name'=>'Dunsin','last_name'=>'Olubobokun','country'=>'Nigeria']);
        $inputKey = "W92ZB837943A711B98D35E799DFE3Z18";
        $iv = "tuqZQhKP48e8Piuc";
        $blockSize = 256;
        $aes = new AESEncryption($data, $inputKey, $iv, $blockSize);
        $enc = $aes->encrypt();
        $aes->setData($enc);
        $dec=$aes->decrypt();
        echo "After encryption: ".$enc."<br/>";
        echo "After decryption: ".$dec."<br/>";
0 голосов
/ 07 октября 2018

Если вы используете PHP> = 7.2, рассмотрите возможность использования встроенного расширения ядра натрия для шифрования.

Более подробную информацию вы найдете здесь - http://php.net/manual/en/intro.sodium.php.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...