首先我为这个答案的长度道歉。
我刚刚遇到了这个帖子,我希望这个课程可以帮助任何阅读这个帖子并寻找他们可以使用的答案和源代码的人。
说明:
此类将首先获取提供的加密密钥,并使用 SHA-512 算法在 PBKDF2 实现中运行 1000 次迭代。
加密数据时,此类将压缩数据并在加密前计算压缩数据的 md5 摘要。它还将计算压缩后的数据长度。然后使用压缩数据对这些计算值进行加密,并将 IV 预先添加到加密输出中。
在每次加密操作之前使用 dev/urandom 生成一个新的 IV。如果脚本在 Windows 机器上运行且 PHP 版本低于 5.3,则该类将使用 MCRYPT_RAND 生成 IV。
根据参数$raw_output是真还是假,加密方法默认返回小写十六进制或加密数据的原始二进制。
解密将反转加密过程并检查计算出的 md5 摘要是否等于存储的使用数据加密的 md5 摘要。如果哈希值不同,解密方法将返回 false。它还将使用压缩数据的存储长度来确保在解压缩之前删除所有填充。
此类在 CBC 模式下使用 Rijndael 128。
该类将跨平台工作,并已在 PHP 5.2、5.3、5.4、5.5 和 5.6 上进行了测试
文件:AesEncryption.php
<?php
/**
* This file contains the class AesEncryption
*
* AesEncryption can safely encrypt and decrypt plain or binary data and
* uses verification to ensure decryption was successful.
*
* PHP version 5
*
* LICENSE: This source file is subject to version 2.0 of the Apache license
* that is available through the world-wide-web at the following URI:
* https://www.apache.org/licenses/LICENSE-2.0.html.
*
* @author Michael Bush <michael(.)bush(@)hotmail(.)co(.)uk>
* @license https://www.apache.org/licenses/LICENSE-2.0.html Apache 2.0
* @copyright 2015 Michael Bush
* @version 1.0.0
*/
/**
* @version 1.0.0
*/
final class AesEncryption
{
/**
* @var string
*/
private $key;
/**
* @var string
*/
private $iv;
/**
* @var resource
*/
private $mcrypt;
/**
* Construct the call optionally providing an encryption key
*
* @param string $key
* @return Encryption
* @throws RuntimeException if the PHP installation is missing critical requirements
*/
public function __construct($key = null) {
if (!extension_loaded ('mcrypt')) {
throw new RuntimeException('MCrypt library is not availble');
}
if (!extension_loaded ('hash')) {
throw new RuntimeException('Hash library is not availble');
}
if (!in_array('rijndael-128', mcrypt_list_algorithms(), true)) {
throw new RuntimeException('MCrypt library does not contain an implementation of rijndael-128');
}
if (!in_array('cbc', mcrypt_list_modes(), true)) {
throw new RuntimeException('MCrypt library does not support CBC encryption mode');
}
$this->mcrypt = mcrypt_module_open('rijndael-128', '', 'cbc', '');
if(isset($key)) {
$this->SetKey($key);
}
}
/**
* @return void
*/
public function __destruct() {
if (extension_loaded ('mcrypt')) {
if (isset($this->mcrypt)) {
mcrypt_module_close($this->mcrypt);
}
}
}
/**
* Set the key to be used for encryption and decryption operations.
*
* @param string $key
* @return void
*/
public function SetKey($key){
$this->key = $this->pbkdf2('sha512', $key, hash('sha512', $key, true), 1000, mcrypt_enc_get_key_size($this->mcrypt), true);
}
/**
* Encrypts data
*
* @param string $data
* @param bool $raw_output if false this method will return lowercase hexit, if true this method will return raw binary
* @return string
*/
public function Encrypt($data, $raw_output = false) {
$data = gzcompress($data, 9);
$hash = md5($data, true);
$datalen = strlen($data);
$datalen = pack('N', $datalen);
$data = $datalen . $hash . $data;
if (version_compare(PHP_VERSION, '5.3.0', '<=')) {
if (strtolower (substr (PHP_OS, 0, 3)) == 'win') {
$this->iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($this->mcrypt), MCRYPT_RAND);
} else {
$this->iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($this->mcrypt), MCRYPT_DEV_URANDOM);
}
} else {
$this->iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($this->mcrypt), MCRYPT_DEV_URANDOM);
}
$this->initialize();
$data = mcrypt_generic($this->mcrypt, $data);
$this->deinitialize();
$data = $this->iv . $data;
$this->iv = null;
if ($raw_output) {
return $data;
}
$data = unpack('H*',$data);
$data = end($data);
return $data;
}
/**
* Decrypts data
*
* @param string $data
* @return string This method will return false if an error occurs
*/
public function Decrypt($data) {
if (ctype_xdigit($data)) {
$data = pack ('H*',$data);
}
$this->iv = substr ($data, 0, mcrypt_enc_get_iv_size($this->mcrypt));
$data = substr ($data, mcrypt_enc_get_iv_size($this->mcrypt));
$this->initialize();
$data = mdecrypt_generic($this->mcrypt, $data);
$this->deinitialize();
$datalen = substr($data, 0, 4);
$len = unpack('N', $datalen);
$len = end($len);
$hash = substr($data, 4, 16);
$data = substr($data, 20, $len);
$datahash = md5($data, true);
if ($this->compare($hash,$datahash)) {
$data = @gzuncompress($data);
return $data;
}
return false;
}
/**
* Initializes the mcrypt module
*
* @return void
*/
private function initialize() {
mcrypt_generic_init($this->mcrypt, $this->key, $this->iv);
}
/**
* Deinitializes the mcrypt module and releases memory.
*
* @return void
*/
private function deinitialize() {
mcrypt_generic_deinit($this->mcrypt);
}
/**
* Implementation of a timing-attack safe string comparison algorithm, it will use hash_equals if it is available
*
* @param string $safe
* @param string $supplied
* @return bool
*/
private function compare($safe, $supplied) {
if (function_exists('hash_equals')) {
return hash_equals($safe, $supplied);
}
$safe .= chr(0x00);
$supplied .= chr(0x00);
$safeLen = strlen($safe);
$suppliedLen = strlen($supplied);
$result = $safeLen - $suppliedLen;
for ($i = 0; $i < $suppliedLen; $i++) {
$result |= (ord($safe[$i % $safeLen]) ^ ord($supplied[$i]));
}
return $result === 0;
}
/**
* Implementation of the keyed-hash message authentication code algorithm, it will use hash_hmac if it is available
*
* @param string $algo
* @param string $data
* @param string $key
* @param bool $raw_output
* @return string
*
* @bug method returning wrong result for joaat algorithm
* @id 101275
* @affects PHP installations without the hash_hmac function but they do have the joaat algorithm
* @action wont fix
*/
private function hmac($algo, $data, $key, $raw_output = false) {
$algo = strtolower ($algo);
if (function_exists('hash_hmac')) {
return hash_hmac($algo, $data, $key, $raw_output);
}
switch ( $algo ) {
case 'joaat':
case 'crc32':
case 'crc32b':
case 'adler32':
case 'fnv132':
case 'fnv164':
case 'fnv1a32':
case 'fnv1a64':
$block_size = 4;
break;
case 'md2':
$block_size = 16;
break;
case 'gost':
case 'gost-crypto':
case 'snefru':
case 'snefru256':
$block_size = 32;
break;
case 'sha384':
case 'sha512':
case 'haval256,5':
case 'haval224,5':
case 'haval192,5':
case 'haval160,5':
case 'haval128,5':
case 'haval256,4':
case 'haval224,4':
case 'haval192,4':
case 'haval160,4':
case 'haval128,4':
case 'haval256,3':
case 'haval224,3':
case 'haval192,3':
case 'haval160,3':
case 'haval128,3':
$block_size = 128;
break;
default:
$block_size = 64;
break;
}
if (strlen($key) > $block_size) {
$key=hash($algo, $key, true);
} else {
$key=str_pad($key, $block_size, chr(0x00));
}
$ipad=str_repeat(chr(0x36), $block_size);
$opad=str_repeat(chr(0x5c), $block_size);
$hmac = hash($algo, ($key^$opad) . hash($algo, ($key^$ipad) . $data, true), $raw_output);
return $hmac;
}
/**
* Implementation of the pbkdf2 algorithm, it will use hash_pbkdf2 if it is available
*
* @param string $algorithm
* @param string $password
* @param string $salt
* @param int $count
* @param int $key_length
* @param bool $raw_output
* @return string
* @throws RuntimeException if the algorithm is not found
*/
private function pbkdf2($algorithm, $password, $salt, $count = 1000, $key_length = 0, $raw_output = false) {
$algorithm = strtolower ($algorithm);
if (!in_array($algorithm, hash_algos(), true)) {
throw new RuntimeException('Hash library does not contain an implementation of ' . $algorithm);
}
if (function_exists('hash_pbkdf2')) {
return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}
$hash_length = strlen(hash($algorithm, '', true));
if ($count <= 0) {
$count = 1000;
}
if($key_length <= 0) {
$key_length = $hash_length * 2;
}
$block_count = ceil($key_length / $hash_length);
$output = '';
for($i = 1; $i <= $block_count; $i++) {
$last = $salt . pack('N', $i);
$last = $xorsum = $this->hmac($algorithm, $last, $password, true);
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = $this->hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if ($raw_output) {
return substr($output, 0, $key_length);
}
$output = unpack('H*',$output);
$output = end ($output);
return substr($output, 0, $key_length);
}
}
示例用法:
<?php
include 'AesEncryption.php';
$key = 'my secret key';
$string = 'hello world';
try
{
$aes = new AesEncryption($key); // exception can be thrown here if the class is not supported
$data = $aes->Encrypt($string, true); // expecting return of a raw byte string
$decr = $aes->Decrypt($data); // expecting the return of "hello world"
var_dump ($decr);
// encrypt something else with a different key
$aes->SetKey('my other secret key'); // exception can be thrown here if the class is not supported
$data2 = $aes->Encrypt($string); // return the return of a lowercase hexit string
$decr = $aes->Decrypt($data2); // expecting the return of "hello world"
var_dump ($decr);
// proof that the key was changed
$decr = $aes->Decrypt($data); // expecting return of Boolean False
var_dump ($decr);
// reset the key back
$aes->SetKey($key); // exception can be thrown here if the class is not supported
$decr = $aes->Decrypt($data); // expecting hello world
var_dump ($decr);
}
catch (Exception $e)
{
print 'Error running AesEncryption class; reason: ' . $e->getMessage ();
}