PHP 支持多种加密解密算法,包含对称加密(可逆)和非对称加密(不可逆)

不可逆加密算法,包含md5、sha1等,可参加米扑博客:php 加密算法md5, sha1

更多加密算法,请见米扑博客:AES、DES、RSA三种典型加密算法强烈推荐

本文主要介绍AES对称加密,用到了 mcrypt 模块

 

AES 算法简介

AES(Advanced Encryption Standard,高级加密标准),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。

这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。

AES对称加密算法的加密和解密的密钥(密钥K)是相同的,米扑科技的项目、微信小程序加密传输等就是用这个加密算法的,具体的加密流程如下图: 

AES是基于数据块的加密方式,每次处理的数据是一块16字节,当数据不是16字节的倍数时填充,这就是所谓的分组密码(区别于基于比特位的流密码),16字节是分组长度。

分组加密有几种方式分别是:

ECB:是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文。

CBC:是一种循环模式,前一个分组的密文和当前分组的明文异或操作后再加密,这样做的目的是增强破解难度。

CFB/OFB实际上是一种反馈模式,目的也是增强破解的难度。

ECB和CBC的加密结果是不一样的,两者的模式不同,而且CBC会在第一个密码块运算时加入一个初始化向量。

 

安装 mcrypt 库包

1. 下载 mcrypt

 官方介绍:http://mcrypt.sourceforge.net

下载 libmcrypt 和 mcrypt:https://sourceforge.net/projects/mcrypt/files/

直接下载最新安装包:libmcrypt-2.5.8.tar.gz 和 mcrypt-2.6.8.tar.gz

 

2. 安装 libmcrypt 和 mcrypt

1)安装 libmcrypt

tar zxvf libmcrypt-2.5.8.tar.gz 
cd libmcrypt-2.5.8
./configure 
make
make install

 

2)安装 mcrypt

tar zxvf mcrypt-2.6.8.tar.gz
cd mcrypt-2.6.8/
ln -s /usr/local/bin/libmcrypt-config /usr/bin/libmcrypt-config
/sbin/ldconfig
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
./configure
make
make install

 

安装错误排查

如果安装过程中,抛出“configure: error: *** libmcrypt was not found”错误

*** 'libmcrypt-config --version' returned 2.4.0, but LIBMCRYPT (2.5.8)
*** was found! If libmcrypt-config was correct, then it is best
*** to remove the old version of LIBMCRYPT. You may also be able to fix the error
*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing
*** /etc/ld.so.conf. Make sure you have run ldconfig if that is
*** required on your system.
*** If libmcrypt-config was wrong, set the environment variable LIBMCRYPT_CONFIG
*** to point to the correct copy of libmcrypt-config, and remove the file config.cache
*** before re-running configure
configure: error: *** libmcrypt was not found

这是因为环境变量的问题,gcc编译的时候根据自身定义的变量寻找相关函数库等文件,libmcrypt也是刚安装的,在变量中没有定义出来,所以手动添加环境变量使其生效:

# ln -s /usr/local/bin/libmcrypt-config /usr/bin/libmcrypt-config

# export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

然后 #./configure 过后,继续 # make && make install , 成功!

更多安装PHP库,详见米扑博客:LNMP(CentOS+Nginx+Mysql+PHP)服务器环境配置 (经典推荐

 

3. 下载安装PHP扩展 mcrypt.so

PHP 7.2 之前,PHP 安装包自带有 mcrypt 库,目录在:/opt/php-5.6.11/ext/mcrypt/

安装步骤:

1)编译安装

cd /opt/php-5.6.11/ext/mcrypt/
/usr/local/php/bin/phpize
./configure
make
make install
Installing shared extensions:     /usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/

2)编辑 php.ini 文件,添加自带的 mcrypt.so 动态库

vim /usr/local/php/etc/php.ini

在末尾添加一行

extension=/usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/mcrypt.so

3)重启 php-fpm 或 apache,使其失效

# /etc/init.d/php-fpm restart
Restarting php-fpm daemon: php-fpm.

 

PHP 7.2 之后,PHP 安装包移除了 mcrypt 库,因此必须自己手动下载 php 库

1)下载 mcrypt-1.0.1.tgz

pecl 官网:http://pecl.php.net/package/mcrypt

安装扩展 mcrypt

tar zxvf mcrypt-1.0.1.tgz
cd mcrypt-1.0.1
/usr/local/php/bin/phpize 
./configure --with-php-config=/usr/local/php/bin/php-config 
make
make install
Installing shared extensions:     /usr/local/php/lib/php/extensions/no-debug-zts-20170718/

2)编辑 php.ini 文件,添加自编译的 mcrypt.so 动态库

vim /usr/local/php/etc/php.ini

在末尾添加一行

extension=/usr/local/php/lib/php/extensions/no-debug-zts-20170718/mcrypt.so

3)重启 php-fpm 或 apache,使其失效

# /etc/init.d/php-fpm restart
Restarting php-fpm daemon: php-fpm.

补充:

php 自带的扩展目录为 /home/data/tool-server/LNMP/php-7.2.8/ext/

编译常见的扩展的动态库如下:

# ll /usr/local/php/lib/php/extensions/no-debug-zts-20170718/
-rwxr-xr-x 1 root root 1201584 Dec 26 22:57 gd.so
-rwxr-xr-x 1 root root 1177792 Dec 19 21:53 imagick.so
-rwxr-xr-x 1 root root 3424944 Dec 26 22:57 mbstring.so
-rwxr-xr-x 1 root root  149504 Dec 27 12:05 mcrypt.so
-rwxr-xr-x 1 root root  386312 Dec 19 21:52 memcached.so
-rwxr-xr-x 1 root root 3530874 Dec 19 21:47 opcache.a
-rwxr-xr-x 1 root root 1972464 Dec 19 21:47 opcache.so
-rwxr-xr-x 1 root root   36728 Dec 19 21:52 pdo_mysql.so
-rwxr-xr-x 1 root root 1827760 Dec 19 21:53 redis.so
-rwxr-xr-x 1 root root  108760 Dec 26 22:55 xmlreader.so
-rwxr-xr-x 1 root root  183920 Dec 26 22:57 xml.so
-rwxr-xr-x 1 root root  118416 Dec 26 22:55 xmlwriter.so
-rwxr-xr-x 1 root root  131016 Dec 26 22:57 xsl.so

 

4. 查看 mcrypt

通过 phpinfo() 查看 mcrypt 动态库

 

检测PHP是否支持 mcrypt 模块,并查看支持哪些加密算法和模式

$cipher_list = mcrypt_list_algorithms();	// mcrypt支持的加密算法列表
$mode_list = mcrypt_list_modes(); 		// mcrypt支持的加密模式列表
var_dump($cipher_list);
var_dump($mode_list);

运行结果:

cipher_list 

array (size=19)
  0 => string 'cast-128' (length=8)
  1 => string 'gost' (length=4)
  2 => string 'rijndael-128' (length=12)
  3 => string 'twofish' (length=7)
  4 => string 'arcfour' (length=7)
  5 => string 'cast-256' (length=8)
  6 => string 'loki97' (length=6)
  7 => string 'rijndael-192' (length=12)
  8 => string 'saferplus' (length=9)
  9 => string 'wake' (length=4)
  10 => string 'blowfish-compat' (length=15)
  11 => string 'des' (length=3)
  12 => string 'rijndael-256' (length=12)
  13 => string 'serpent' (length=7)
  14 => string 'xtea' (length=4)
  15 => string 'blowfish' (length=8)
  16 => string 'enigma' (length=6)
  17 => string 'rc2' (length=3)
  18 => string 'tripledes' (length=9)

其中 rijndael-128、rijndael-192、rijndael-256都是AES算法

 

mode_list

array (size=8)
  0 => string 'cbc' (length=3)
  1 => string 'cfb' (length=3)
  2 => string 'ctr' (length=3)
  3 => string 'ecb' (length=3)
  4 => string 'ncfb' (length=4)
  5 => string 'nofb' (length=4)
  6 => string 'ofb' (length=3)
  7 => string 'stream' (length=6)

 

 

AES算法加密模式和填充方式

AES支持五中模式:CBC、CFB、ECB、OFB、PCBC

AES支持三种填充:NoPadding、PKCS5Padding、ISO10126Padding,不支持SSL3Padding,不支持“NONE”模式

其中AES/ECB/NoPadding和米扑科技的项目现在使用的AESUtil得出的结果相同(在16的整数倍情况下)

不带模式和填充来获取AES算法的时候,其默认使用ECB/PKCS5Padding

 

算法/模式/填充                    16字节加密后数据长度         不满16字节加密后长度

AES/CBC/NoPadding             16                         不支持
AES/CBC/PKCS5Padding          32                         16
AES/CBC/ISO10126Padding       32                          16
AES/CFB/NoPadding             16                          原始数据长度
AES/CFB/PKCS5Padding          32                          16
AES/CFB/ISO10126Padding       32                          16
AES/ECB/NoPadding             16                          不支持
AES/ECB/PKCS5Padding          32                          16
AES/ECB/ISO10126Padding       32                          16
AES/OFB/NoPadding             16                          原始数据长度
AES/OFB/PKCS5Padding          32                          16
AES/OFB/ISO10126Padding       32                          16
AES/PCBC/NoPadding            16                          不支持
AES/PCBC/PKCS5Padding         32                          16
AES/PCBC/ISO10126Padding      32                          16

可以看到,在原始数据长度为16的整数倍时,假如原始数据长度等于16*n,则使用NoPadding时加密后数据长度等于16*n,其它情况下加密数据长度等于16*(n+1)。在不足16的整数倍的情况下,假如原始数据长度等于16*n+m[其中m小于16],除了NoPadding填充之外的任何方式,加密数据长度都等于16*(n+1);NoPadding填充情况下,CBC、ECB、PCBC三种模式是不支持的,CFB、OFB两种模式下则加密数据长度等于原始数据长度。

本文示例采用的 AES/CBC/NoPadding

 

PHP 对称加密AES算法应用示例

// AES 加密
function encrypt_aes($string='', $key='mimvp') {
	$cipher = MCRYPT_RIJNDAEL_128;
	$mode = MCRYPT_MODE_CBC;
	$iv = substr(md5($key), 0, 16);
	
	$private_key = substr(md5($key), 0, 32);		// 必须为16,24,32个字符
	$encrypted = mcrypt_encrypt($cipher, $private_key, $string, $mode, $iv);
	return base64_encode($encrypted);
}

// AES 解密
function decrypt_aes($string='', $key='mimvp') {
	$string = base64_decode($string);
	
	$cipher = MCRYPT_RIJNDAEL_128;
	$mode = MCRYPT_MODE_CBC;
	$iv = substr(md5($key), 0, 16);
	
	$private_key = substr(md5($key), 0, 32);		// 必须为16,24,32个字符
	$decrypted = rtrim(mcrypt_decrypt($cipher, $private_key, $string, $mode, $iv), "\0");
	
	return $decrypted;
}

$data = 'I love mimvp.com';
$data_encrypt = encrypt_aes($data);
echo '<br><br> encrypt_aes : ' . $data_encrypt;
echo '<br><br> decrypt_aes : ' . decrypt_aes($data_encrypt);


$data = 'I love mimvp.com in 2016.09.12 @mimvp.com $1,000,000,000';
$data_encrypt = encrypt_aes($data);
echo '<br><br> encrypt_aes : ' . $data_encrypt;
echo '<br><br> decrypt_aes : ' . decrypt_aes($data_encrypt);

 

为了方便与其他语言进行数据交换,也可以手动指定 $iv 为一个固定的可见字符串,但是长度要与 mcrypt_create_iv 生成的一致,否则会报一个 The IV parameter must be as long as the blocksize in 的警告。这里的长度是16,所以固定的字符串常量也要是16个字符,可以利用代码生成其16位的随机字符串(注意:加密和解密的都必须一致,可在函数参数里传递此参数)

$iv = mcrypt_create_iv(mcrypt_get_iv_size($cipher, $mode), MCRYPT_RAND);

 

运行结果:

encrypt_aes : HsjNQpu+/yxeMNmaErqiqA==
decrypt_aes : I love mimvp.com
encrypt_aes : HsjNQpu+/yxeMNmaErqiqPLjk/N4VmlhYlnrjZCcnZhwJPoML1eTNG+cLwFUxFv8Se+xOkRddaM5c7umF+npLQ==
decrypt_aes : I love mimvp.com in 2016.09.12 @mimvp.com $1,000,000,000

从加密解密结果可见:

1)AES 加密算法字符串会跟着加密字符串的长度增加而增加,不同于md5等是固定的长度,更多算法长度参见米扑博客《php 加密算法md5, sha1

2)AES 加密算法,支持任意字符的加密,如特殊字符 @、$、空格、逗号等

3)注意私钥字符串,长度必须为16,24,32位,且加密解密的私钥需一致

 

 

参考推荐

php 加密算法md5, sha1

Python 常用加密算法 base64, md5, sha1

Python中Base64编码和解码

AES、DES、RSA三种典型加密算法

AES 加密算法的详细介绍与实现

PHP 使用cookie实现记住登录状态

PHP Session与Cookie详解

公钥,私钥,数字签名的通俗理解

LNMP(CentOS+Nginx+Mysql+PHP)服务器环境配置 (推荐