1. 文章
  2. 文章详情

php AES加密解密详解

PHP AES的加密解密

AES加密算法

密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
解释来源:http://baike.so.com/doc/6783134-6999702.html
参考:http://www.docin.com/p-572103142.html

PHP 开发API接口签名验证 中我们说到了sign签名,sign其实是防篡改的一种方法,它将约定好的排序、位置、数组进行密钥加密生成sign对比。
是的,sign签名我们是能看到数据的,只是可以防止数据的篡改。而AES可以加密解密数据
AES通过约定好的密钥进行加密,通过约定好的密钥解密。

ECB加密模式(不推荐):

php AES加密解密

容易被攻击

function encrypt($input, $key) {

$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);

$input = pkcs5_pad($input, $size);

$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');

$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);

mcrypt_generic_init($td, hextobin($key), $iv);

$data = mcrypt_generic($td, $input);

mcrypt_generic_deinit($td);

mcrypt_module_close($td);

$data = base64_encode($data);

return $data;

}

function pkcs5_pad($text, $blocksize) {

$pad = $blocksize - (strlen($text) % $blocksize);

return $text . str_repeat(chr($pad), $pad);

}

/* * 解密 */

function decrypt($sStr, $sKey) {

$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, hextobin($sKey), base64_decode($sStr), MCRYPT_MODE_ECB);

$dec_s = strlen($decrypted);

$padding = ord($decrypted[$dec_s - 1]);

$decrypted = substr($decrypted, 0, -$padding);

return $decrypted;

}

function hextobin($hexstr) {

$n = strlen($hexstr);

$sbin = "";

$i = 0;

while ($i < $n) {

$a = substr($hexstr, $i, 2);

$c = pack("H*", $a);

if ($i == 0) {

$sbin = $c;

} else {

$sbin.=$c;

}

$i+=2;

}

return $sbin;

}

define('SECRETKEY', '3163213543213543052abc43edfedus');

//Warning: mcrypt_decrypt(): Key of size 9 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported

//Warning: pack(): Type H: illegal hex digit s

//Warning: pack(): Type H: illegal hex digit u

# 加密

echo $endata= encrypt('Hello, world! ', SECRETKEY);

# 解密

echo $dedata= decrypt($endata, SECRETKEY);

php AES加密解密


有没有注意到 代码中的注释:
Warning: mcrypt_decrypt(): Key of size 9 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported

Warning: pack(): Type H: illegal hex digit s Warning: pack(): Type H: illegal hex digit u

因为加密的密钥有长度限制所以必须为16、24、32的长度才可以
pack() 16进制转换为二进制的时候,发现了key密钥中包含了“s,u” 字符,想想看,16进制最多到F,F以后的字符都是扯蛋
1111 = 8 + 4 + 2 + 1 = 15 =F 1110 = 8 + 4 + 2 + 0 = 14= E 1101 = 8 + 4 + 0 + 1 = 13= D 1100 = 8 + 4 + 0 + 0 = 12 =C 1011 = 8 + 0 + 2 + 1 = 11= B 1010 = 8 + 0 + 2 + 0 = 10 =A

所以生成使用密钥的时候,我们应该预先生成好16进制的密钥。
加密后:
4ihVrJk0acwmAF6td5emIkV9T6VnRHYZcW2BUw4CSUQ=

解密后:
Hello, world!

CBC加密模式 (推荐……):

php AES加密解密



按 Ctrl+C 复制代码
按 Ctrl+C 复制代码
打印效果:
tZOYOkwxFdkqP6chub5Q8SsH9igXPIKlCNmWxVBjFkM=

Hello, world!

使用PKCS5Padding/PKCS7Padding填充可以兼容多平台语言之间AES加密解密(PHP、Java、C……)
注意这里每次产生的密文是相同的,因为设置了初试向量iv为16位个数的“0”。要产生不同的密文就要使用变化的初试向量iv


define('SECRETKEY', '12f862d21d3ceafba1b88e5f22960d55');

/** * 加密方法 * @param string $str * @return string */

function encrypt($str) {

//AES, 128 ECB模式加密数据 $str = addPKCS7Padding($str);

$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);

define('A', $iv);

$encrypt_str = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, SECRETKEY, $str, MCRYPT_MODE_CBC, $iv);

return base64_encode($encrypt_str); }

/** * 解密方法 * @param string $str * @return string */

function decrypt($str) {

//AES, 128 ECB模式加密数据 $str = base64_decode($str);

$encrypt_str = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, SECRETKEY, $str, MCRYPT_MODE_CBC,A);

$encrypt_str = stripPKSC7Padding($encrypt_str);

return $encrypt_str; }

/** * 填充算法 * @param string $source * @return string */

function addPKCS7Padding($source) {

$source = trim($source);

$block = mcrypt_get_block_size('rijndael-128', 'cbc');

$pad = $block - (strlen($source) % $block);

if ($pad <= $block) {

$char = chr($pad);

$source .= str_repeat($char, $pad);

}

return $source;

}

/** * 移去填充算法 * @param string $source * @return string */

function stripPKSC7Padding($source) {

$char = substr($source, -1);

$num = ord($char);

$source = substr($source, 0, -$num);

return $source;

}

/** * 加密 */

$string =encrypt('Hello, world!');

print_r($string); echo '


';

/** * 解密 */

$string=decrypt($string);

print_r($string);



打印效果:
faxcY9iEj4Q+67l2Fd+k+URMp8Y4VVih/JeUSQbYuwKw3u8holL0mdG3Jg51mbPxDFlj76M3dU8jYt2nhtUhxA==

tVWCmXvNuOeuWYI6VoSplUtdq+yV66vDr22Bdja2uNVpaftNNU6jwQWth2DD4JxgaL1d3Atv8jZGsoSV6XLJWA==

PTjgu8kJ1B7LTm40IXEl6fPVTbQYuETPvjg2miQVVs1i/r+07pjGAfsL5JpSQKOROMX8B3mSiSu/YjubHffkYA==

Cb5WjMwb3wOpqCmUWEErPc9sfUEYYiMzvoosYoHdCFq1vh78batnXLbpjOavnCT0Y6y+4jq+fviTmY3plOAK8g==

结果都是:
Hello, world!

注意: 这里面的密钥define('SECRETKEY', '12f862d21d3ceafba1b88e5f22960d55');
长度为32位,但在严格的AES中,加密的密钥必须与字符串算法长度一至,(MCRYPT_RIJNDAEL_128、MCRYPT_RIJNDAEL_192、MCRYPT_RIJNDAEL_256)
1 bit 位 = 1 二进制数据 1 byte 字节 = 8 bit 1 字母 = 1 byte = 8 bit

也就是128位/8=16字节=16字符
192位/8=24字节=24字符
256位/8=32字节=32字符

所以,上面的代码为了兼容其他Java、C等,密钥应该改为16位:
define('SECRETKEY', '12f862d21d3ceafb')


也可以直接生成一个32位的16进制密钥用于其他功能的加密(sign签名),通过hex2bin可以将32位的16进制字符串转换为16位二进制字符串
生成16进制随机码


$num="";

for($i=0;$i<32;$i++){

$num .=dechex(rand(0,15));

}

echo $num;

echo $screct_key = hex2bin($num);

echo strlen($screct_key);


打印:
9957a20d097485d608d9f090efe34b9a

�W� t�������K�

16

参考:https://zhuanlan.zhihu.com/p/41736709

发表评论

登录后才能评论

评论列表(0条)