AES加解密 对称加密
美国国家标准和技术研究所(NIST)经过三轮候选算法筛选,从众多的分组密码中选中Rijndael算法作为高级加密标准(AES)。
三个基础概念:
秘钥,填充,模式
一、 密钥
密钥是AES算法实现加密和解密的根本。对称加密算法之所以对称,是因为这类算法对明文的加密和解密需要使用同一个密钥。
AES支持三种长度的密钥:128位,192位,256位
AES128,AES192,AES256,实际上就是指的AES算法对不同长度密钥的使用。
AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。密钥的长度不同,推荐加密轮数也不同,如下表所示:
AES | 密钥长度(32位比特字) | 分组长度(32位比特字) | 加密轮数 |
AES-128 | 4 |
4 |
10 |
AES-192 | 6 |
4 |
12 |
AES-256 | 8 |
4 | 14 |
这里实现的是AES-128,也就是密钥的长度为128位,加密轮数为10轮。
上面说到,AES的加密公式为C = E(K,P),在加密函数E中,会执行一个轮函数,并且执行10次这个轮函数,这个轮函数的前9次执行的操作是一样的,只有第10次有所不同。也就是说,一个明文分组会被加密10轮。AES的核心就是实现一轮中的所有操作。
AES对称加密:
二、填充:
PKCS5Padding(默认):
在PKCS5Padding中,明确定义Block的大小是8位,只对于8字节(BlockSize=8)进行填充,填充内容为0x01-0x08;
如果明文块少于16个字节(128bit),在明文块末尾补足相应数量的字符,且每个字节的值等于缺少的字符数。
例如如明文:{1,2,3,4,5,a,b,c,d,e},缺少6个字节,则补全为{1,2,3,4,5,a,b,c,d,e,6,6,6,6,6,6}
PKCS7Padding:
PKCS7Padding定义中,对于块的大小是不确定的,可以在1-255之间。
ZeroPADDING:
剩余部分填充0
NoPadding:
不做任何填充,但是要求明文必须是16字节的整数倍。
分组密码的填充
流密码:
三、 四种模式:
3.1 ECB模式
优点:
1.简单;
2.有利于并行计算;
3.误差不会被传送;
缺点:
1.不能隐藏明文的模式;
2.可能对明文进行主动攻击;
3.2 CBC模式:
优点:
1.不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。
缺点:
1.不利于并行计算;
2.误差传递;
3.需要初始化向量IV
3.3 CFB模式:
优点:
1.隐藏了明文模式;
2.分组密码转化为流模式;
3.可以及时加密传送小于分组的数据;
缺点:
1.不利于并行计算;
2.误差传送:一个明文单元损坏影响多个单元;
3.唯一的IV;
3.4 OFB模式:
优点:
1.隐藏了明文模式;
2.分组密码转化为流模式;
3.可以及时加密传送小于分组的数据;
缺点:
1.不利于并行计算;
2.对明文的主动攻击是可能的;
3.误差传送:一个明文单元损坏影响多个单元;
PHP:
/*
data:待加密的明文信息数据。
method:密码学方式。openssl_get_cipher_methods() 可获取有效密码方式列表。
key,key。
options 是以下标记的按位或:OPENSSL_RAW_DATA 、 OPENSSL_ZERO_PADDING:
1、0
2、OPENSSL_RAW_DATA=1 (推荐)用PKCS#7进行补位,返回原始数据,base64_encode后和0的结果相同。
3、OPENSSL_ZERO_PADDING=2 AES-128-CBC的加密结果为空,可换成其他模式
4、OPENSSL_NO_PADDING=3
iv:非 NULL 的初始化向量。
tag:使用 AEAD 密码模式(GCM 或 CCM)时传引用的验证标签。
aad:附加的验证数据。
tag_length:验证 tag 的长度。GCM 模式时,它的范围是 4 到 16。
* */
$data = '白杨,不错,不错,不错,小小很乖。';
$method = 'AES-128-CFB';
$key = "约定的密码";
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($method));
// opetions:0
echo "opetions:0".PHP_EOL;
$encrypted = openssl_encrypt($data, $method,$key, 0, $iv);
echo $encrypted.PHP_EOL; //输出:sABO6CZqu8MYGCrPvnNkxEf9c48Fw7UeNnvuanZVDc4OGoRy1XpPM6lprvVQBpDtpzUKnYZBtWNhE7ySr7naaQ==
$decrypted = openssl_decrypt($encrypted, $method,$key, 0, $iv);
echo $decrypted.PHP_EOL.PHP_EOL; //输出:白杨,不错,不错,不错,小小很乖。
// opetions:OPENSSL_RAW_DATA 1
echo "opetions:OPENSSL_RAW_DATA".PHP_EOL;
$encrypted = openssl_encrypt($data, $method,$key, 1, $iv);
echo $encrypted.PHP_EOL;//输出:几行��I��Vnz��这样的原始数
$decrypted = openssl_decrypt($encrypted, $method,$key, 1, $iv);
echo $decrypted.PHP_EOL.PHP_EOL;//输出:白杨,不错,不错,不错,小小很乖。
// opetions:OPENSSL_ZERO_PADDING 2
echo "opetions:OPENSSL_ZERO_PADDING".PHP_EOL;
$encrypted = openssl_encrypt($data, $method,$key, 2, $iv);
$encrypted = base64_encode($encrypted);
echo $encrypted.PHP_EOL;
$decrypted = openssl_decrypt(base64_decode($encrypted), $method,$key, 2, $iv);
echo $decrypted.PHP_EOL.PHP_EOL;
// opetions:OPENSSL_NO_PADDING 3
echo "opetions:OPENSSL_NO_PADDING".PHP_EOL;
$str_padded = $data;
if (strlen($str_padded) % 16) {
$str_padded = str_pad($str_padded,strlen($str_padded) + 16 - strlen($str_padded) % 16, "\0");
}
$encrypted = openssl_encrypt($str_padded, $method, $key, OPENSSL_NO_PADDING,$iv);
$encrypted = base64_encode($encrypted);
echo $encrypted.PHP_EOL;
$m = openssl_decrypt( base64_decode($encrypted) , $method, $key, OPENSSL_NO_PADDING,$iv);
echo rtrim( rtrim( $m,chr(0) ), chr(7) ).PHP_EOL;