web-dev-qa-db-fra.com

PHP AES chiffrer/déchiffrer

J'ai trouvé un exemple pour en/décodage de chaînes en PHP. Au début, ça a l'air très bon mais ça ne marchera pas :-(

Quelqu'un sait-il quel est le problème?

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

Le résultat est:

Crypté: boKRNTYYNp7AiOvY1CidqsAn9wX4ufz/D9XrpjAOPk8=

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

40
Andreas Prang

$sDecrypted et $sEncrypted n'étaient pas définis dans votre code. Voir une solution qui fonctionne (mais n'est pas sécurisé!):


ARRÊTEZ!

Cet exemple est insecure! Ne l'utilise pas!


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

Mais il y a d'autres problèmes dans ce code qui le rendent peu sûr, notamment l'utilisation de ECB (qui n'est pas un mode encryption, seulement un bloc de construction sur lequel des modes de cryptage peuvent être définis). Voir réponse de Fab Sa pour une solution rapide aux pires problèmes et réponse de Scott pour savoir comment le faire correctement.

52
zz1433

Veuillez utiliser une bibliothèque de chiffrement existante secure PHP

C'est généralement une mauvaise idée d'écrire votre propre cryptographie, à moins que vous n'ayez l'expérience nécessaire pour casser les implémentations de cryptographie d'autres personnes.

Aucun des exemples ici n'authentifie le texte chiffré , ce qui les rend vulnérables aux attaques par réécriture de bits.

Si vous pouvez installer des extensions PECL, libsodium est encore meilleur

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

Puis pour le tester:

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

Cela peut être utilisé dans toutes les situations où vous transmettez des données au client (par exemple, des cookies cryptés pour des sessions sans stockage côté serveur, des paramètres d'URL cryptés, etc.) avec un degré de certitude raisonnablement élevé que l'utilisateur final ne peut pas déchiffrer ou falsifier de manière fiable. avec ça.

Puisque libsodium est multiplate-forme , cela facilite également la communication avec PHP à partir de, par exemple. Applets Java ou applications mobiles natives.


Remarque: Si vous avez spécifiquement besoin d'ajouter des cookies cryptés alimentés par libsodium à votre application, mon employeur Paragon Initiative Enterprises est en train de développer une bibliothèque appelée Halite qui fait tout cela à votre place.

68

Pour information, MCRYPT_MODE_ECB n'utilise pas le vecteur d'initialisation (IV). Le mode ECB divise votre message en blocs et chaque bloc est crypté séparément. Je vraiment ne le recommande pas.

Le mode CBC utilise le vecteur IV pour rendre chaque message unique. CBC est recommandé et devrait être utilisé à la place de la BCE.

Exemple :

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

Vous devez stocker le IV pour décoder chaque message (IV sont pas secrets). Chaque message est unique car chaque message a un IV unique.

25
Fabien Sa

Si vous ne voulez pas utiliser une dépendance lourde pour obtenir une résolution de 15 lignes de code, utilisez les fonctions intégrées OpenSSL . La plupart des installations PHP sont livrées avec OpenSSL, qui fournit un cryptage AES rapide, compatible et sécurisé en PHP. Eh bien, c'est sécurisé tant que vous suivez les meilleures pratiques.

Le code suivant:

  • utilise AES256 en mode CBC
  • est compatible avec d'autres implémentations AES, mais pas mcrypt , mcrypt utilisant PKCS # 5 au lieu de PKCS # 7.
  • génère une clé à partir du mot de passe fourni à l'aide de SHA256
  • génère un hachage hmac des données cryptées pour le contrôle d'intégrité
  • génère un IV aléatoire pour chaque message
  • ajoute l'IV (16 octets) et le hachage (32 octets) au texte chiffré
  • devrait être assez sécurisé

IV est une information publique et doit être aléatoire pour chaque message. Le hachage garantit que les données n'ont pas été altérées.

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

Usage:

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

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

Quelques points importants à noter avec le cryptage AES: 

  1. Ne jamais utiliser du texte brut comme clé de cryptage. Toujours hacher la clé en texte brut, puis utiliser pour le cryptage. 
  2. Toujours utiliser Random IV (vecteur d'initialisation) pour le cryptage et le décryptage. La vraie randomisation est importante. 
  3. Comme mentionné ci-dessus, n'utilisez pas ecb mode, utilisez plutôt CBC.
2
Navneet Kumar

Si vous utilisez MCRYPT_RIJNDAEL_128, essayez rtrim($output, "\0\3"). Si la longueur de la chaîne est inférieure à 16, la fonction de déchiffrement renverra une chaîne de 16 caractères, en ajoutant 03 à la fin.

Vous pouvez facilement vérifier cela, par exemple. en essayant:

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

echo bin2hex($decrypted_string)."=".bin2hex("TheString");
1
Kamen

J'utilise le code d'objet pour CCAVenue Payment Gateway

class AES {

    public function encrypt($plainText, $key) {
        $secretKey = $this->hextobin(md5($key));
        $initVector = pack("C*", 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f);
        $openMode = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', '');
        $blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc');
        $plainPad = $this->pkcs5_pad($plainText, $blockSize);
        if (mcrypt_generic_init($openMode, $secretKey, $initVector) != -1) {
            $encryptedText = mcrypt_generic($openMode, $plainPad);
            mcrypt_generic_deinit($openMode);
        }
        return bin2hex($encryptedText);
    }

    public function decrypt($encryptedText, $key) {
        $secretKey = $this->hextobin(md5($key));
        $initVector = pack("C*", 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f);
        $encryptedText = $this->hextobin($encryptedText);
        $openMode = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', '');
        mcrypt_generic_init($openMode, $secretKey, $initVector);
        $decryptedText = mdecrypt_generic($openMode, $encryptedText);
        $decryptedText = rtrim($decryptedText, "\0");
        mcrypt_generic_deinit($openMode);
        return $decryptedText;
    }

    //*********** Padding Function *********************

    public function pkcs5_pad($plainText, $blockSize) {
        $pad = $blockSize - (strlen($plainText) % $blockSize);
        return $plainText . str_repeat(chr($pad), $pad);
    }

    //********** Hexadecimal to Binary function for php 4.0 version ********

    public function hextobin($hexString) {
        $length = strlen($hexString);
        $binString = "";
        $count = 0;
        while ($count < $length) {
            $subString = substr($hexString, $count, 2);
            $packedString = pack("H*", $subString);
            if ($count == 0) {
                $binString = $packedString;
            } else {
                $binString .= $packedString;
            }

            $count += 2;
        }
        return $binString;
    }

}

Utilisateur du code

$obj = new AES();
$key = "XXXXXXXXXXXXXXXX";
$plainText = "Hello World";
$encryptedText = $obj->encrypt($plainText, $key);
$rcvdString=$obj->decrypt($encryptedText,$key);
0
Nanhe Kumar

Si vous utilisez PHP> = 7.2, envisagez d'utiliser l'extension de noyau de sodium intégrée pour le cryptage. 

Trouvez plus d'informations ici - http://php.net/manual/en/intro.sodium.php.

0
M_R_K