PHP AES encrypt / decrypt

Znalazłem przykład en / dekodowania łańcuchów w PHP. Na początku wygląda bardzo dobrze, ale nie zadziała : - (

Czy ktoś wie w czym problem?
$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)));
}

Wynik jest następujący:

boKRNTYYNp7AiOvY1CidqsAn9wX4ufz/D9XrpjAOPk8=

Odszyfrowane: —‚(ÑÁ ^ yË~F'¸®Ó–í œð2Á_B‰Â—

Author: Scott Arciszewski, 2010-08-06

10 answers

$sDecrypted i $sEncrypted były niezdefiniowane w Twoim kodzie. Zobacz Rozwiązanie, które działa (, ale nie jest bezpieczne!):


Przestań!

Ten przykład jest niepewny!Nie używaj go!


$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"
    );
}

Istnieją jednak inne problemy, które sprawiają, że kod ten nie jest bezpieczny, w szczególności użycie trybu ECB (który nie jest trybem szyfrowania, a jedynie elementem składowym, na którym można zdefiniować tryby szyfrowania). Zobacz odpowiedź Fab Sa na szybki napraw najgorszych problemów i odpowiedź Scotta Jak to zrobić dobrze.

 58
Author: zz1433,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-10 05:49:00

Proszę użyć istniejącej bezpiecznej biblioteki szyfrowania PHP

Generalnie nie jest dobrym pomysłem pisanie własnej kryptografii, chyba że masz doświadczenie w łamaniu implementacji kryptografii innych ludzi.

Żaden z przykładów tutaj nie uwierzytelnia szyfrogramu, co czyni go podatnym na ataki przepisywania bitów.

Jeśli możesz zainstalować rozszerzenia PECL, libsodium jest jeszcze lepsze

<?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
    );
}    

Następnie przetestować:

<?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);

To może być stosowany w każdej sytuacji, w której przekazujesz dane do klienta (np. zaszyfrowane pliki cookie dla sesji bez przechowywania po stronie serwera, zaszyfrowane parametry URL itp.) z dość dużym stopniem pewności, że użytkownik końcowy nie może go odszyfrować lub niezawodnie manipulować.

Ponieważ libsodium jest wieloplatformowe , ułatwia to również komunikację z PHP, np. z apletów Javy lub natywnych aplikacji mobilnych.


Uwaga: jeśli potrzebujesz dodać zaszyfrowany cookies powered by libsodium to your app, my employer Paragon Initiative Enterprises rozwija bibliotekę o nazwie Halite , która robi to wszystko za Ciebie.

 83
Author: Scott Arciszewski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-15 08:55:26

Jeśli nie chcesz używać dużej zależności dla czegoś rozwiązywalnego w 15 liniach kodu, użyj wbudowanych funkcji OpenSSL. Większość instalacji PHP jest wyposażona w OpenSSL, który zapewnia szybkie, kompatybilne i bezpieczne szyfrowanie AES w PHP. Jest bezpieczny, dopóki stosujesz najlepsze praktyki.

Następujący kod:

  • używa AES256 w trybie CBC
  • jest kompatybilny z innymi implementacjami AES, ale nie mcrypt, ponieważ mcrypt używa PKCS # 5 zamiast PKCS # 7.
  • generuje klucz z podanego hasła za pomocą SHA256
  • generuje HMAC hash zaszyfrowanych danych w celu sprawdzenia integralności
  • generuje losową IV dla każdej wiadomości
  • prepends The IV (16 bajtów) and the hash (32 bajtów) to the ciphertext
  • powinno być całkiem bezpieczne

IV jest informacją publiczną i musi być losowa dla każdej wiadomości. Hash zapewnia, że dane nie zostały zmienione z.

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 . $iv, $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_equals(hash_hmac('sha256', $ciphertext . $iv, $key, true), $hash)) return null;

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

Użycie:

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

echo decrypt($encrypted, 'password');
// decrypt($encrypted, 'wrong password') === null

Edit: Zaktualizowano do użycia hash_equals i dodano IV do hasha.

 55
Author: blade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-05 09:43:59

Dla informacji MCRYPT_MODE_ECB nie używa IV (wektora inicjalizacji). Tryb ECB podziel wiadomość na bloki i każdy blok jest szyfrowany oddzielnie. Naprawdę nie polecam tego .

Tryb CBC użyj kroplówki, aby każda wiadomość była unikalna. CBC jest zalecany i powinien być stosowany zamiast EBC.

Przykład:

<?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");
}

Musisz zaopatrzyć IV, aby rozszyfrować każdą wiadomość(IV są , a nie tajne). Każda wiadomość jest unikalna, ponieważ każda wiadomość ma unikalny IV.

 27
Author: Fabien Sa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-18 12:55:34

Kilka ważnych rzeczy do odnotowania z szyfrowaniem AES:

  1. nigdy nie używaj zwykłego tekstu jako klucza szyfrującego. Zawsze hashuj zwykły klawisz tekstowy, a następnie użyj do szyfrowania.
  2. Zawsze używaj Random IV (wektor inicjalizacji) do szyfrowania i deszyfrowania. prawdziwa randomizacja jest ważna.
  3. Jak wspomniano powyżej, nie używaj trybu EBC, zamiast tego używaj trybu CBC.
 3
Author: Navneet Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-28 11:45:21

Jest to rozwiązanie robocze AES encryption - zaimplementowane przy użyciu openssl. Wykorzystuje tryb szyfrowania blokowego (CBC-Mode). Tak więc obok data i key można podać iv i 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!');
                }
            }

        }

Przykładowe użycie:

<?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/>";
 3
Author: Dunsin Olubobokun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-03 07:28:59

Jeśli używasz MCRYPT_RIJNDAEL_128, spróbuj rtrim($output, "\0\3"). Jeśli długość łańcucha jest mniejsza niż 16, funkcja decrypt zwróci łańcuch o długości 16 znaków, dodając 03 na końcu.

Możesz to łatwo sprawdzić, np. próbując:

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

echo bin2hex($decrypted_string)."=".bin2hex("TheString");
 1
Author: Kamen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-28 17:03:09

Są to kompaktowe metody szyfrowania / deszyfrowania łańcuchów za pomocą PHP przy użyciu AES256 CBC:

function encryptString($plaintext, $password, $encoding = null) {
    $iv = openssl_random_pseudo_bytes(16);
    $ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, $iv);
    $hmac = hash_hmac('sha256', $ciphertext.$iv, hash('sha256', $password, true), true);
    return $encoding == "hex" ? bin2hex($iv.$hmac.$ciphertext) : ($encoding == "base64" ? base64_encode($iv.$hmac.$ciphertext) : $iv.$hmac.$ciphertext);
}

function decryptString($ciphertext, $password, $encoding = null) {
    $ciphertext = $encoding == "hex" ? hex2bin($ciphertext) : ($encoding == "base64" ? base64_decode($ciphertext) : $ciphertext);
    if (!hash_equals(hash_hmac('sha256', substr($ciphertext, 48).substr($ciphertext, 0, 16), hash('sha256', $password, true), true), substr($ciphertext, 16, 32))) return null;
    return openssl_decrypt(substr($ciphertext, 48), "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, substr($ciphertext, 0, 16));
}

Użycie:

$enc = encryptString("mysecretText", "myPassword");
$dec = decryptString($enc, "myPassword");
 1
Author: Marco Concas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-26 22:28:41

Oto ulepszona wersja oparta na kodzie napisanym przez blade

  • add comments
  • zastąp argumenty przed wyrzuceniem, aby uniknąć wycieku tajemnic z wyjątkiem
  • sprawdzanie wartości zwracanych z funkcji openssl i HMAC

Kod:

class Crypto
{
    /**
     * Encrypt data using OpenSSL (AES-256-CBC)
     * @param string $plaindata Data to be encrypted
     * @param string $cryptokey key for encryption (with 256 bit of entropy)
     * @param string $hashkey key for hashing (with 256 bit of entropy)
     * @return string IV+Hash+Encrypted as raw binary string. The first 16
     *     bytes is IV, next 32 bytes is HMAC-SHA256 and the rest is
     *     $plaindata as encrypted.
     * @throws Exception on internal error
     *
     * Based on code from: https://stackoverflow.com/a/46872528
     */
    public static function encrypt($plaindata, $cryptokey, $hashkey)
    {
        $method = "AES-256-CBC";
        $key = hash('sha256', $cryptokey, true);
        $iv = openssl_random_pseudo_bytes(16);

        $cipherdata = openssl_encrypt($plaindata, $method, $key, OPENSSL_RAW_DATA, $iv);

        if ($cipherdata === false)
        {
            $cryptokey = "**REMOVED**";
            $hashkey = "**REMOVED**";
            throw new \Exception("Internal error: openssl_encrypt() failed:".openssl_error_string());
        }

        $hash = hash_hmac('sha256', $cipherdata.$iv, $hashkey, true);

        if ($hash === false)
        {
            $cryptokey = "**REMOVED**";
            $hashkey = "**REMOVED**";
            throw new \Exception("Internal error: hash_hmac() failed");
        }

        return $iv.$hash.$cipherdata;
    }

    /**
    * Decrypt data using OpenSSL (AES-256-CBC)
     * @param string $encrypteddata IV+Hash+Encrypted as raw binary string
     *     where the first 16 bytes is IV, next 32 bytes is HMAC-SHA256 and
     *     the rest is encrypted payload.
     * @param string $cryptokey key for decryption (with 256 bit of entropy)
     * @param string $hashkey key for hashing (with 256 bit of entropy)
     * @return string Decrypted data
     * @throws Exception on internal error
     *
     * Based on code from: https://stackoverflow.com/a/46872528
     */
    public static function decrypt($encrypteddata, $cryptokey, $hashkey)
    {
        $method = "AES-256-CBC";
        $key = hash('sha256', $cryptokey, true);
        $iv = substr($encrypteddata, 0, 16);
        $hash = substr($encrypteddata, 16, 32);
        $cipherdata = substr($encrypteddata, 48);

        if (!hash_equals(hash_hmac('sha256', $cipherdata.$iv, $hashkey, true), $hash))
        {
            $cryptokey = "**REMOVED**";
            $hashkey = "**REMOVED**";
            throw new \Exception("Internal error: Hash verification failed");
        }

        $plaindata = openssl_decrypt($cipherdata, $method, $key, OPENSSL_RAW_DATA, $iv);

        if ($plaindata === false)
        {
            $cryptokey = "**REMOVED**";
            $hashkey = "**REMOVED**";
            throw new \Exception("Internal error: openssl_decrypt() failed:".openssl_error_string());
        }

        return $plaindata;
    }
}

Jeśli naprawdę nie możesz mieć odpowiedniego szyfrowania i kluczy hashowych, ale musisz użyć hasła wpisanego przez użytkownika jako jedynej tajemnicy, możesz zrobić coś takiego:

/**
 * @param string $password user entered password as the only source of
 *   entropy to generate encryption key and hash key.
 * @return array($encryption_key, $hash_key) - note that PBKDF2 algorithm
 *   has been configured to take around 1-2 seconds per conversion
 *   from password to keys on a normal CPU to prevent brute force attacks.
 */
public static function generate_encryptionkey_hashkey_from_password($password)
{
    $hash = hash_pbkdf2("sha512", "$password", "salt$password", 1500000);
    return str_split($hash, 64);
}
 1
Author: Mikko Rantalainen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-20 10:21:22

Jeśli używasz PHP >= 7.2 rozważ użycie wbudowanego rozszerzenia rdzenia sodowego do kodowania.

Więcej informacji znajdziesz tutaj - http://php.net/manual/en/intro.sodium.php.

 0
Author: M_R_K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-07 13:07:35