Filtres de chiffrement

Avertissement

Cette fonctionnalité est OBSOLÈTE à partir de PHP 7.1.0. Dépendre de cette fonctionnalité est fortement déconseillé.

Les filtres de chiffrement sont particulièrement utiles pour le chiffrement des fichiers/flux.

mcrypt.* et mdecrypt.* fournissent un système de chiffrement symétrique basé sur libmcrypt. Les deux filtres supportent les mêmes algorithmes disponibles avec la bibliothèque mcrypt, sous la forme de la directive mcrypt.ciphername où ciphername est le nom du chiffrement qui sera passé à mcrypt_module_open(). Les cinq paramètres suivants sont aussi disponibles :

Paramètres du filtre mcrypt
Paramètre Obligatoire Par défaut Valeurs possibles
mode Optionnel cbc cbc, cfb, ecb, nofb, ofb, stream
algorithms_dir Optionnel ini_get('mcrypt.algorithms_dir') Chemin vers les algorithmes de modules
modes_dir Optionnel ini_get('mcrypt.modes_dir') Chemin vers les modes de modules
iv Obligatoire N/A Généralement 8, 16 ou 32 octets de données binaires. Dépend du chiffrement
key Obligatoire N/A Généralement 8, 16 ou 32 octets de données binaires. Dépend du chiffrement

Exemple #1 Chiffrement/déchiffrement avec Blowfish

<?php
//$key supposé être précédemment généré
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);
$fp = fopen('encrypted-file.enc', 'wb');
fwrite($fp, $iv);
$opts = array('mode'=>'cbc','iv'=>$iv, 'key'=>$key);
stream_filter_append($fp, 'mcrypt.blowfish', STREAM_FILTER_WRITE, $opts);
fwrite($fp, 'message to encrypt');
fclose($fp);

//déchiffrement...
$fp = fopen('encrypted-file.enc', 'rb');
$iv = fread($fp, $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC));
$opts = array('mode'=>'cbc','iv'=>$iv, 'key'=>$key)
stream_filter_append($fp, 'mdecrypt.blowfish', STREAM_FILTER_READ, $opts);
$data = rtrim(stream_get_contents($fp));//trims off null padding
fclose($fp);
echo 
$data;
?>

Exemple #2 Chiffrer un fichier en utilisant AES-128 CBC avec SHA256 HMAC en PHP 5.5+

<?php
AES_CBC
::encryptFile($password, "plaintext.txt", "encrypted.enc");
AES_CBC::decryptFile($password, "encrypted.enc", "decrypted.txt");

class 
AES_CBC
{
   protected static 
$KEY_SIZES = array('AES-128'=>16,'AES-192'=>24,'AES-256'=>32);
   protected static function 
key_size() { return self::$KEY_SIZES['AES-128']; } //default AES-128
   
public static function encryptFile($password, $input_stream, $aes_filename){
      
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
      
$fin = fopen($input_stream, "rb");
      
$fc = fopen($aes_filename, "wb+");
      if (!empty(
$fin) && !empty($fc)) {
         
fwrite($fc, str_repeat("_", 32) );//placeholder, SHA256 HMAC will go here later
         
fwrite($fc, $hmac_salt = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM));
         
fwrite($fc, $esalt = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM));
         
fwrite($fc, $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM));
         
$ekey = hash_pbkdf2("sha256", $password, $esalt, $it=1000, self::key_size(), $raw=true);
         
$opts = array('mode'=>'cbc', 'iv'=>$iv, 'key'=>$ekey);
         
stream_filter_append($fc, 'mcrypt.rijndael-128', STREAM_FILTER_WRITE, $opts);
         
$infilesize = 0;
         while (!
feof($fin)) {
            
$block = fread($fin, 8192);
            
$infilesize+=strlen($block);
            
fwrite($fc, $block);
         }
         
$block_size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
         
$padding = $block_size - ($infilesize % $block_size);//$padding is a number from 1-16
         
fwrite($fc, str_repeat(chr($padding), $padding) );//perform PKCS7 padding
         
fclose($fin);
         
fclose($fc);
         
$hmac_raw = self::calculate_hmac_after_32bytes($password, $hmac_salt, $aes_filename);
         
$fc = fopen($aes_filename, "rb+");
         
fwrite($fc, $hmac_raw);//overwrite placeholder
         
fclose($fc);
      }
   }
   public static function 
decryptFile($password, $aes_filename, $out_stream) {
      
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
      
$hmac_raw = file_get_contents($aes_filename, false, NULL,  0, 32);
      
$hmac_salt = file_get_contents($aes_filename, false, NULL, 32, $iv_size);
      
$hmac_calc = self::calculate_hmac_after_32bytes($password, $hmac_salt, $aes_filename);
      
$fc = fopen($aes_filename, "rb");
      
$fout = fopen($out_stream, 'wb');
      if (!empty(
$fout) && !empty($fc) && self::hash_equals($hmac_raw,$hmac_calc)) {
         
fread($fc, 32+$iv_size);//skip sha256 hmac and salt
         
$esalt = fread($fc, $iv_size);
         
$iv    = fread($fc, $iv_size);
         
$ekey = hash_pbkdf2("sha256", $password, $esalt, $it=1000, self::key_size(), $raw=true);
         
$opts = array('mode'=>'cbc', 'iv'=>$iv, 'key'=>$ekey);
         
stream_filter_append($fc, 'mdecrypt.rijndael-128', STREAM_FILTER_READ, $opts);
         while (!
feof($fc)) {
            
$block = fread($fc, 8192);
            if (
feof($fc)) {
               
$padding = ord($block[strlen($block) - 1]);//assume PKCS7 padding
               
$block = substr($block, 0, 0-$padding);
            }
            
fwrite($fout, $block);
         }
         
fclose($fout);
         
fclose($fc);
      }
   }
   private static function 
hash_equals($str1, $str2) {
      if(
strlen($str1) == strlen($str2)) {
         
$res = $str1 ^ $str2;
         for(
$ret=0,$i = strlen($res) - 1; $i >= 0; $i--) $ret |= ord($res[$i]);
         return !
$ret;
      }
      return 
false;
   }
   private static function 
calculate_hmac_after_32bytes($password, $hsalt, $filename) {
      static 
$init=0;
      
$init or $init = stream_filter_register("user-filter.skipfirst32bytes", "FileSkip32Bytes");
      
$stream = 'php://filter/read=user-filter.skipfirst32bytes/resource=' . $filename;
      
$hkey = hash_pbkdf2("sha256", $password, $hsalt, $iterations=1000, 24, $raw=true);
      return 
hash_hmac_file('sha256', $stream, $hkey, $raw=true);
   }
}
class 
FileSkip32Bytes extends php_user_filter
{
   private 
$skipped=0;
   function 
filter($in, $out, &$consumed, $closing)  {
      while (
$bucket = stream_bucket_make_writeable($in)) {
         
$outlen = $bucket->datalen;
         if (
$this->skipped<32){
            
$outlen = min($bucket->datalen,32-$this->skipped);
            
$bucket->data = substr($bucket->data, $outlen);
            
$bucket->datalen = $bucket->datalen-$outlen;
            
$this->skipped+=$outlen;
         }
         
$consumed += $outlen;
         
stream_bucket_append($out, $bucket);
      }
      return 
PSFS_PASS_ON;
   }
}
class 
AES_128_CBC extends AES_CBC {
   protected static function 
key_size() { return self::$KEY_SIZES['AES-128']; }
}
class 
AES_192_CBC extends AES_CBC {
   protected static function 
key_size() { return self::$KEY_SIZES['AES-192']; }
}
class 
AES_256_CBC extends AES_CBC {
   protected static function 
key_size() { return self::$KEY_SIZES['AES-256']; }
}