介绍
介绍
福哥需要在php里使用AES加密解密功能,今天整理出来和大家分享一下。
早期的PHP实现AES借助的是mcrypt扩展,后来在PHP7之后就换成了openssl扩展来实现了。mcrypt版本代码比较复杂且需要自己实现PKCS7补位的逻辑,而openssl版本则默认使用了PKCS7补位不需要自己来编写代码实现了。
通过openssl实现
安装openssl扩展
需要安装php扩展openssl,具体方法就不提供了,php的扩展的安装方式都一样,php7.1以上的版本支持了openssl模块。
加密解密对象
加密解密对象,默认 AES-256-CBC 方法。
class AES_Encrypt{ const BLOCK_SIZE = 32; private string $method; public function __construct(string $method = null){ if($method == null){ $method = "AES-256-CBC"; } $this->method = $method; } 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 = AES_Encrypt::BLOCK_SIZE - ($text_length % AES_Encrypt::BLOCK_SIZE); if ($amount_to_pad == 0) { $amount_to_pad = AES_Encrypt::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); $encrypted = openssl_encrypt($data, $this->method, $key,OPENSSL_ZERO_PADDING, $iv); return $encrypted; } public function decrypt(string $data, string $key, string $iv):string { $decrypted = openssl_decrypt($data, $this->method, $key,OPENSSL_ZERO_PADDING, $iv); $data = $this->pkcs7Decode($decrypted); return $data; } }
options
openssl_encrypt和openssl_decrypt的第三个参数是options,它有着很重要的作用,我们来了解一下。
0:默认模式,自动进行 pkcs7 补位,同时自动进行 base64 编码
1:OPENSSL_RAW_DATA,自动进行 pkcs7 补位, 但是不自动进行 base64 编码
2:OPENSSL_ZERO_PADDING,需要自己进行 pkcs7 补位,同时自动进行 base64 编码
通过mcrypt实现
安装mcrypt扩展
需要安装php扩展mcrypt,具体方法就不提供了,php的扩展的安装方式都一样,php7.1以下的版本支持mcrypt模块。
加密解密对象
加密解密对象,默认 AES-128-CBC 方法。
class AES_Encrypt{ const BLOCK_SIZE = 32; private $RIJNDAEL; private $MODE; 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 = AES_Encrypt::BLOCK_SIZE - ($text_length % AES_Encrypt::BLOCK_SIZE); if ($amount_to_pad == 0) { $amount_to_pad = AES_Encrypt::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); $based_encrypted = base64_encode($encrypted); return $based_encrypted; } public function decrypt($data, $key, $iv){ $data = base64_decode($data); $encrypted = mcrypt_decrypt($this->RIJNDAEL, $key, $data, $this->MODE, $iv); $encrypted = $this->pkcs7Decode($encrypted); return $encrypted; } }
使用
使用AES加密需要原数据、AES私钥、令牌,下面给出一个例子。
注意:在openssl版本里的AES-256-CBC方法对应mcrypt版本里的AES-128-CBC,也是微信公众号服务器接口使用的AES算法的方法
示例
准备一个字符串,加密后再解密,最后如果和原字符串相同,证明函数工作正常。
$dataOrg = "我是福哥,I like coding!!!"; $AESKey = "rN6LfP9qbILPabc938IixdFds3s5ksIqjcPyYxOPx4v"; $iv = ""; // 初始化 $myAES_Encrypt = new AES_Encrypt(); // 加密字符串 $dataEncrypted = $myAES_Encrypt->encrypt($dataOrg, $AESKey, $iv); // 解密字符串 $dataDecrypted = $myAES_Encrypt->decrypt($dataEncrypted, $AESKey, $iv); // 比较 var_dump($dataOrg == $dataDecrypted);
总结
福哥今天给出了两个使用PHP实现AES的加密算法功能的方法,新版本的openssl效率更高、代码更简单,福哥推荐这种方法~~