18

Oct

2011

Classe pour faire de l'AES-256 en Php PDF Imprimer Envoyer

Après plusieurs recherches sur le net pour faire dialoguer PHP avec une application Java qui utilise le cryptage AES-256, je vous offre une classe toute faite.

Elle prend en charge l'AES 128, 192, 256, selon la longeur de la clé que vous passerez, et tous les autres mode supporté par MCrypt.

Pour crypter en AES-256,  on utilise l algorithme "MCRYPT_RIJNDAEL_128" en mode "MCRYPT_MODE_CBC" avec une clé de 32.

 

AES a une valeur fixe pour le "block size" et l'IV de 128 bits (16 chars).
La seule valeur changeante est la taille de la clé selon le cryptage voulu:
- AES 128 (key_size: 16, block_size: 16, iv_size: 16)
- AES 192 (key_size: 24, block_size: 16, iv_size: 16)
- AES 256 (key_size: 32, block_size: 16, iv_size: 16)

 

Explication longue en anglais (je me suis basé dessus pour faire la version courte):

http://www.chilkatsoft.com/p/php_aes.asp

Explications sur les modes d'opérations (ECB, CBC, CFB...)
http://fr.wikipedia.org/wiki/Mode_d%27op%C3%A9ration_%28cryptographie%29

 

Cette classe a aussi une implémentation simple de la fonction OpenSSL "EVP_BytesToKey" qui n'existe pas en PHP et qui permet de dériver une donnée pour renvoyer une clé et un vecteur d'initialisation.

Etant donné qu'elle ne correspond pas tout à fait à la fonction originelle, je l'ai nommé "deriveKeyAndIV".

 

 

 

la classe et l'exemple d'utilisation:

 

 

<?php
/**
 * Class - SimpleCrypt
 * Permet un cryptage grace à Mcrypt
 *
 * Pour crypter en AES :
 *   on utilise l algo "MCRYPT_RIJNDAEL_128" en mode "MCRYPT_MODE_CBC" avec une clé de 32.
 *
 * Explication rapide:
 *  AES a une valeur fixe pour le "block size" et l'IV de 128 bits (16 chars).
 *  La seule valeur changeante est la taille de la clé selon le cryptage voulu:
 *   - AES 128 (key_size: 16, block_size: 16, iv_size: 16)
 *   - AES 192 (key_size: 24, block_size: 16, iv_size: 16)
 *   - AES 256 (key_size: 32, block_size: 16, iv_size: 16)
 *
 *  Comparatif avec RIJNDAEL:
 *   - RIJNDAEL 128 (key_size: 32, block_size: 16, iv_size: 16)
 *   - RIJNDAEL 192 (key_size: 32, block_size: 24, iv_size: 24)
 *   - RIJNDAEL 256 (key_size: 32, block_size: 32, iv_size: 32)
 *
 * Explication longue en anglais (me suis basé dessus pour faire la version courte):
 * @link http://www.chilkatsoft.com/p/php_aes.asp
 *
 * Explications sur les modes d'opérations (ECB, CBC, CFB...)
 * @link http://fr.wikipedia.org/wiki/Mode_d%27op%C3%A9ration_%28cryptographie%29
 *
 * @author Vlyan (www.vlyan.com)
 * @version 1.0
 */
class SimpleCrypt
{
    /**
     * mode par defaut de MCrypt
     * @var String
     */
    const PAD_ZEROBYTE = 'ZeroByte';
 
    /**
     * mode par defaut d'OpenSSL
     * @var String
     */
    const PAD_PKCS7    = 'PKCS7';
 
 
    /**
     * Resource de cryptage Mcrypt
     * @var Ressource
     */
    protected $_rMcrypt;
 
    /**
     * Cle secrete
     * @var String
     */
    protected $_sKey;
 
    /**
     * Vecteur d'Initialisation
     * @var String
     */
    protected $_sIv;
 
    /**
     * Mode de padding
     *  ZeroByte : mode par defaut de MCrypt
     *  PKCS7    : mode par defaut d'OpenSSL
     *
     * @var String
     */
    protected $_sPaddingMode = self::PAD_ZEROBYTE;
 
 
    /**
     * Crypte un texte
     *
     * @param  String $sSecretKey
     * @param  String $sInitVecor
     * @param  String $algorithm
     * @param  String $mode
     * @return SimpleCrypt
     */
    public function __construct($sSecretKey, $sInitVecor = null, $algorithm = MCRYPT_RIJNDAEL_128, $mode = MCRYPT_MODE_CBC) {
        // Charge un chiffrement
        $this->_rMcrypt = mcrypt_module_open($algorithm, '', $mode, '');
 
        if ($this->_rMcrypt) {
            // Crée le vecteur d'initialisation
            $iIvSize = mcrypt_enc_get_iv_size($this->_rMcrypt);
            if (!empty($sInitVecor) && strlen($sInitVecor) > 2) {
                // si trop court, on ajoute des x
                if (strlen($sInitVecor) <= $iIvSize) {
                    $sInitVecor = str_pad($sInitVecor, $iIvSize, 'x');
                }
                // retaille a la bonne longeur
                $this->_sIv = substr($sInitVecor, 0, $iIvSize);
 
            } else {
                // vi aléatoire
                srand();
                $this->_sIv = mcrypt_create_iv($iIvSize, MCRYPT_RAND);
            }
 
            // cle pas asser grande ?
            $iKeySize = mcrypt_enc_get_key_size($this->_rMcrypt);
            if (strlen($sSecretKey) < $iKeySize) {
                // si trop court, on ajoute des x
                $sSecretKey = str_pad($sSecretKey, $iKeySize, 'x');
            }
            $this->_sKey = substr($sSecretKey, 0, $iKeySize);
        }
    }
 
    /**
     * Crypte un texte
     *
     * @param  String $sTexte
     * @return String|False
     */
    public function code($sTexte) {
        if (!empty($sTexte) && !empty($this->_rMcrypt) && !empty($this->_sKey)) {
            if ($this->_sPaddingMode == self::PAD_PKCS7) {
                $sTexte = self::paddingPKCS7Add($sTexte, mcrypt_enc_get_block_size($this->_rMcrypt));
            }
            mcrypt_generic_init($this->_rMcrypt, $this->_sKey, $this->_sIv);
            return mcrypt_generic($this->_rMcrypt, $sTexte);
        }
        return false;
    }
 
    /**
     * Décrypte un texte
     *
     * @param  String $sTexte
     * @return String|False
     */
    public function decode($sTexte) {
        if (!empty($sTexte) && !empty($this->_rMcrypt) && !empty($this->_sKey)) {
            mcrypt_generic_init($this->_rMcrypt, $this->_sKey, $this->_sIv);
            $sTexteOut = mdecrypt_generic($this->_rMcrypt, $sTexte);
            if ($this->_sPaddingMode == self::PAD_PKCS7) {
                $sTexteOut = self::paddingPKCS7Remove($sTexteOut);
            } else {
                $sTexteOut = trim($sTexteOut);
            }
            return $sTexteOut;
        }
        return false;
    }
 
 
    // + + + Methodes statiques + + +
    /**
     * Implémentation simple de la fonction OpenSSL EVP_BytesToKey()
     * retourne une clé et un iv dérivé selon plusieurs paramètres.
     *
     * @link http://www.openssl.org/docs/crypto/EVP_BytesToKey.html
     *
     * @param  String $sData     les données qui vont servir a creer la cle et le vecteur dérivé
     * @param  String $sSalt     un grain de sel
     * @param  Int    $iCount    le nombre d iterations à effectuer
     * @param  String $sCipher   l'algo de cryptage ( ex: MCRYPT_RIJNDAEL_128 )
     * @param  String $sMode     le mode de cryptage ( MCRYPT_MODE_* )
     * @param  String $sHashFunc la fonction de hashing a utiliser ( sha1|md5.. ) ou toute fonction presente dans hash_algos()
     * @return Array|False       retourne un tableau avec 'key' et 'iv' ou false
     */
    public static function deriveKeyAndIV($sData, $sSalt, $iCount, $sCipher = MCRYPT_RIJNDAEL_128, $sMode = MCRYPT_MODE_CBC, $sHashFunc = 'sha1') {
        // input check
        $aAlgos = mcrypt_list_algorithms();
        $aModes = mcrypt_list_modes();
        $aHashs = hash_algos();
        if (empty($sMode) || !in_array($sMode, $aModes)
        || empty($sCipher) || !in_array($sCipher, $aAlgos)
        || empty($sHashFunc) || !in_array($sHashFunc, $aHashs)
        || empty($sData) || $iCount < 1
        ) {
            return false;
        }
 
        $iIv  = mcrypt_get_iv_size($sCipher, $sMode);
        $iKey = mcrypt_get_key_size($sCipher, $sMode);
        if ($iKey < 1) return false;
 
        $iDtl  = 0;              // data length
        $iWhl  = 1;              // while index, debut a 1 pour que d-1 = 0
        $aData = array(0 => ''); // array 0 doit être vide, mais initialisé
 
        // D_i = HASH^count(D_(i-1) || data || salt)
        do {
            $aData[$iWhl]  = $aData[$iWhl-1];  // D_(i-1)
            $aData[$iWhl] .= $sData;           // data
            if (!empty($sSalt)) {
                $aData[$iWhl] .= $sSalt;       // salt
            }
 
            // Digest Raw Hash
            for ($iSha = 0 ; $iSha < $iCount ; $iSha++) {
                $aData[$iWhl] = hash($sHashFunc, $aData[$iWhl], true);
            }
 
            $iDtl += strlen($aData[$iWhl]);
            $iWhl++;
 
        } while ($iDtl < ($iKey + $iIv) && $iWhl < 100);
 
        $sData = implode('', $aData);
 
        if (strlen($sData) < ($iKey + $iIv)) {
            return false;
        }
 
        // retourne les x premiers caracteres pour la cle
        // retourne les x caracteres suivant pour l IV
        return array(
    		'key' => substr($sData, 0, $iKey),
    		'iv'  => substr($sData, $iKey, $iIv),
        );
    }
 
    /**
     * Ajoute le padding PKCS7
     * @param  String $sStr
     * @param  Int    $iBlocksize
     * @return String
     */
    public static function paddingPKCS7Add($sStr, $iBlocksize) {
        $iPad = $iBlocksize - (strlen($sStr) % $iBlocksize);
        return $sStr.str_repeat(chr($iPad), $iPad);
    }
 
    /**
     * Enleve le padding PKCS7
     * @param  String $sStr
     * @return String
     */
    public static function paddingPKCS7Remove($sStr) {
        $iLgt = strlen($sStr);
        $iPad = ord($sStr[$iLgt-1]);
        return substr($sStr, 0, -$iPad);
    }
 
 
    // + + + Gets / Sets + + +
    /**
     * Retourne l IV
     * @return String
     */
    public function getIv() {
        return $this->_sIv;
    }
 
    /**
     * Retourne la Cle
     * @return String
     */
    public function getKey() {
        return $this->_sKey;
    }
 
    /**
     * Retourne le mode de padding
     * @return String
     */
    public function getPaddingMode() {
        return $this->_sPaddingMode;
    }
 
    /**
     * Assigne le mode de padding
     * @param  String $sMode
     * @return String|False  Renvois l ancien mode de padding, ou false
     */
    public function setPaddingMode($sMode) {
        if (!empty($sMode) && in_array($sMode, array(self::PAD_ZEROBYTE, self::PAD_PKCS7))) {
            $sOld = $this->_sPaddingMode;
            $this->_sPaddingMode = $sMode;
            return $sOld;
        }
        return false;
    }
 
}//end class
 
 
 
// exemple d'utilisation: AES-256
 
$sDatas = 'mon texte a encoder';
 
// clé aléatoire de 32 caractères non imprimable
$sKey   = hash('sha256', uniqid('', true), true);
 
// dérive la clé pour avoir une clé & iv
$aDeriv = SimpleCrypt::deriveKeyAndIV($sKey, 'A3s,Z4?', 7);
 
// construit l'objet
$oCrypt = new SimpleCrypt($aDeriv['key'], $aDeriv['iv']);
 
// met le mode de padding en PKCS7
$oCrypt->setPaddingMode(SimpleCrypt::PAD_PKCS7);
 
// on crypte
$sDatasCrypte = $oCrypt->code($sDatas);
 
// ...
 
// on décrypte
$sDatasDecrypte = $oCrypt->decode($sDatasCrypte);
 
 
// affichage
echo "Clé d'origine : $sKey";
echo "<br>Clé dérivée : {$aDeriv['key']}";
echo "<br>IV dérivé : {$aDeriv['iv']}";
echo "<hr>Texte d'entrée : $sDatas";
echo "<hr>Texte crypté : $sDatasCrypte";
echo "<hr>Texte décrypté : $sDatasDecrypte";
 

 

J'espère que cela vous sera utile :)

 

 
French (Fr)English (United Kingdom)
oct.11

Interface personnalisée

Left4Run 1.0.1
avr.09

Lanceur graphique pour Left4Dead

nov.08

Lanceur graphique pour les serveurs DoL.

nov.08

Programme alternatif au ComponentChooser pour les personnes ayant le .net et ne voulant pas insta...