一、介绍
早期的 PHP 实现 AES 借助的是 mcrypt 扩展,后来在 PHP7 之后就换成了 openssl 扩展来实现了。
mcrypt 版本代码比较复杂且需要自己实现 PKCS7 补位的逻辑,而 openssl 版本则默认使用了 PKCS7 补位不需要自己来编写代码实现了。
二、openssl
加密解密对象,默认 AES-256-CBC 方法
需要安装 php 扩展 openssl,具体方法就不提供了,php 的扩展的安装方式都一样,php7.1 以上的版本支持了 openssl 模块
class aesHelper extends baseHelper
{
const BLOCK_SIZE = 32;
private $method;
public function __construct(string $method = null)
{
if($method == null){
$method = "AES-256-CBC";
}
$this->method = $method;
}
private function pkcs7Encode($text): string
{
$text_length = strlen($text);
$amount_to_pad = self::BLOCK_SIZE - ($text_length % self::BLOCK_SIZE);
if ($amount_to_pad == 0) {
$amount_to_pad = self::BLOCK_SIZE;
}
$pad_chr = chr($amount_to_pad);
$tmp = "";
for ($index = 0; $index < $amount_to_pad; $index++) {
$tmp .= $pad_chr;
}
return $text . $tmp;
}
private function pkcs7Decode($text)
{
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > 32) {
$pad = 0;
}
return substr($text, 0, (strlen($text) - $pad));
}
public function encrypt(string $data, string $key, string $iv):string
{
$data = $this->pkcs7Encode($data);
return openssl_encrypt($data, $this->method, $key,OPENSSL_ZERO_PADDING, $iv);
}
public function decrypt(string $data, string $key, string $iv):string
{
$decrypted = openssl_decrypt($data, $this->method, $key,OPENSSL_ZERO_PADDING, $iv);
return $this->pkcs7Decode($decrypted);
}
}
测试
$aesKey = 'lulublog';
$iv = 'L2gZjtlri1KB4hGn';
$aesHelper = new aesHelper('AES-256-CBC');
$encrypt = $aesHelper->encrypt('lulublog欢迎您', $aesKey, $iv);
var_dump($encrypt);
$decrypt = $aesHelper->decrypt($encrypt, $aesKey, $iv);
var_dump($decrypt);
注意
初始化向量 IV 是 16 位的字符串
ECB 不需要初始化向量 IV,即 $iv=''
新版本的 openssl 效率更高、代码更简单
三、mcrypt
加密解密对象,默认 AES-128-CBC 方法
需要安装 php 扩展 mcrypt,具体方法就不提供了,php 的扩展的安装方式都一样,php7.1 以下的版本支持mcrypt 模块
class aesMcryptHelper extends baseHelper
{
const BLOCK_SIZE = 32;
private $RIJNDAEL;
private $MODE;
private $methodArr;
public function __construct($method = null)
{
if($method == null){
$method = "AES-128-CBC";
}
$this->RIJNDAEL = null;
$this->MODE = null;
$this->methodArr = explode("-", $method);
switch($this->methodArr[1]){
case "128":
$this->RIJNDAEL = MCRYPT_RIJNDAEL_128;
break;
case "192":
$this->RIJNDAEL = MCRYPT_RIJNDAEL_192;
break;
case "256":
$this->RIJNDAEL = MCRYPT_RIJNDAEL_256;
break;
}
switch($this->methodArr[2]){
case "CBC":
$this->MODE = MCRYPT_MODE_CBC;
break;
case "CFB":
$this->MODE = MCRYPT_MODE_CFB;
break;
case "ECB":
$this->MODE = MCRYPT_MODE_ECB;
break;
case "NOFB":
$this->MODE = MCRYPT_MODE_NOFB;
break;
case "OFB":
$this->MODE = MCRYPT_MODE_OFB;
break;
case "STREAM":
$this->MODE = MCRYPT_MODE_STREAM;
break;
}
if($this->RIJNDAEL == null || $this->MODE == null){
throw new Exception("invalid RIJNDAEL or MODE about '". $method. "'", 2000100);
}
}
public function pkcs7Pad($str)
{
$len = mb_strlen($str, '8bit');
$c = 16 - ($len % 16);
$str .= str_repeat(chr($c), $c);
return $str;
}
private function pkcs7Encode($text)
{
$text_length = strlen($text);
$amount_to_pad = self::BLOCK_SIZE - ($text_length % self::BLOCK_SIZE);
if ($amount_to_pad == 0) {
$amount_to_pad = self::BLOCK_SIZE;
}
$pad_chr = chr($amount_to_pad);
$tmp = "";
for ($index = 0; $index < $amount_to_pad; $index++) {
$tmp .= $pad_chr;
}
return $text . $tmp;
}
private function pkcs7Decode($text)
{
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > 32) {
$pad = 0;
}
return substr($text, 0, (strlen($text) - $pad));
}
public function encrypt($data, $key, $iv)
{
$data = $this->pkcs7Encode($data);
$encrypted = @mcrypt_encrypt($this->RIJNDAEL, $key, $data, $this->MODE, $iv);
return base64_encode($encrypted);
}
public function decrypt($data, $key, $iv)
{
$data = base64_decode($data);
$encrypted = @mcrypt_decrypt($this->RIJNDAEL, $key, $data, $this->MODE, $iv);
return $this->pkcs7Decode($encrypted);
}
}
测试
$aesKey = 'lulubloglulublog';
$iv = 'L2gZjtlri1KB4hGn';
$aesMcryptHelper = new aesMcryptHelper('AES-128-CBC');
$encrypt = $aesMcryptHelper->encrypt('lulublog欢迎您', $aesKey, $iv);
var_dump($encrypt);
$decrypt = $aesMcryptHelper->decrypt($encrypt, $aesKey, $iv);
var_dump($decrypt);
注意
aesKey 的长度为:16、24、32