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 :)
|