Filtros de encriptación

Los filtros de cifrado son especialmente útiles para el cifrado de archivos y flujos.

mcrypt.* y mdecrypt.*

Advertencia

Esta característica ha sido declarada OBSOLETA a partir de PHP 7.0.1. Su uso está totalmente desaconsejado.

mcrypt.* y mdecrypt.* ofrecen encripción y desencripción simétrica usando libmcrypt. Ambos conjuntos de filtros soportan los mismos algoritmos disponibles a la extensión mcrypt en la forma de mcrypt.ciphername, en donde ciphername es el nombre del cifrado que debe ser pasado a mcrypt_module_open(). Los siguientes cinco parámetros se encuentran disponibles también:

parámetros del filtro mcrypt
Parámetro ¿Requerido? Por omisión Valores de ejemplo
mode Optional cbc cbc, cfb, ecb, nofb, ofb, stream
algorithms_dir Optional ini_get('mcrypt.algorithms_dir') Ubicación de los módulos de los algoritmos
modes_dir Optional ini_get('mcrypt.modes_dir') Ubicación de los módulos de los modos
iv Required N/A Típicamente 8, 16, o 32 bytes de datos binarios. Depende del cifrado
key Required N/A Típicamente 8, 16, o 32 bytes de datos binarios. Depende del cifrado

Ejemplo #1 Encriptar/Desencriptar con Blowfish

<?php
//$key que se supone que ha sido generado previamente
$iv_size mcrypt_get_iv_size(MCRYPT_BLOWFISHMCRYPT_MODE_CBC);
$iv mcrypt_create_iv($iv_sizeMCRYPT_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);

//desencriptar...
$fp fopen('encrypted-file.enc''rb');
$iv fread($fp$iv_size mcrypt_get_iv_size(MCRYPT_BLOWFISHMCRYPT_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;
?>

Ejemplo #2 Encriptar el archivo usando AES-128 CBC con 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_128MCRYPT_MODE_CBC);
      
$fin fopen($input_stream"rb");
      
$fc fopen($aes_filename"wb+");
      if (!empty(
$fin) && !empty($fc)) {
         
fwrite($fcstr_repeat("_"32) );//marcador de posición, SHA256 HMAC irá aquí más tarde
         
fwrite($fc$hmac_salt mcrypt_create_iv($iv_sizeMCRYPT_DEV_URANDOM));
         
fwrite($fc$esalt mcrypt_create_iv($iv_sizeMCRYPT_DEV_URANDOM));
         
fwrite($fc$iv mcrypt_create_iv($iv_sizeMCRYPT_DEV_URANDOM));
         
$ekey hash_pbkdf2("sha256"$password$esalt$it=1000self::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($fin8192);
            
$infilesize+=strlen($block);
            
fwrite($fc$block);
         }
         
$block_size mcrypt_get_block_size(MCRYPT_RIJNDAEL_128MCRYPT_MODE_CBC);
         
$padding $block_size - ($infilesize $block_size);//$padding es un número del 1 al 16
         
fwrite($fcstr_repeat(chr($padding), $padding) );//realizar el relleno de PKCS7
         
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);//sobrescribir el marcador de posición
         
fclose($fc);
      }
   }
   public static function 
decryptFile($password$aes_filename$out_stream) {
      
$iv_size mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128MCRYPT_MODE_CBC);
      
$hmac_raw file_get_contents($aes_filenamefalseNULL,  032);
      
$hmac_salt file_get_contents($aes_filenamefalseNULL32$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($fc32+$iv_size);//skip sha256 hmac and salt
         
$esalt fread($fc$iv_size);
         
$iv    fread($fc$iv_size);
         
$ekey hash_pbkdf2("sha256"$password$esalt$it=1000self::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($fc8192);
            if (
feof($fc)) {
               
$padding ord($block[strlen($block) - 1]);//asume el relleno del PKCS7
               
$block substr($block00-$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=100024$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']; }
}