AES 5种加密模式 && 初始向量的影响

  • 引言
  • AES的工作模式
  • ECB模式(电子密码本模式:Electronic codebook)
  • CBC模式(密码分组链接:Cipher-block chaining)
  • CFB模式(密文反馈:Cipher feedback)
  • OFB模式(输出反馈:Output feedback)
  • CTR模式 (计数器:Counter)


引言

写这篇文章是由于,关于如何使用AES的文章很多,大多都是API调用,但是关于IV初始向量的作用好像没几个文章有写,IV初始向量CBCCFB模式下,仅影响前16字节(128位加密方式)的块,推荐使用CTR模式。

AES的工作模式

AES的工作模式(ECBCBCCFBOFBCTR)。

  • OPENSSL_ZERO_PADDING 你可以使用OPENSSL_ZERO_PADDINGflag,让它自动填充0x00(使用自动填充返回的内容就直接是base64编码过的了,并不是二进制数据),虽然也可以再base64Decode变会二进制数据,但是这样做感觉像是多此一举,但是由于php不建议使用OPENSSL_ZERO_PADDING,实际上会无效,还是需要你手动补齐,它唯一的作用就是直接返回base64格式的编码。
  • OPENSSL_NO_PADDING 推荐使用OPENSSL_NO_PADDING手动填充,我们可以自写填充函数,好处是我们可以自己选择需要二进制数据还是base64数据。
  • OPENSSL_RAW_DATAOPENSSL_RAW_DATA不填充数据。

ECB模式(电子密码本模式:Electronic codebook)

加密前根据加密块大小分成若干块,之后将每块使用相同的密钥单独加密,解密同理。

<?php
	
$data = '0123456789abcdefghijklmnopqrstuvwxyz';
$key = "0123456789abcdef";

$encode = base64_encode(openssl_encrypt($data,"AES-128-ECB",$key,OPENSSL_RAW_DATA));
$decode = openssl_decrypt(base64_decode($encode),"AES-128-ECB",$key,OPENSSL_RAW_DATA);

echo $encode.PHP_EOL;
echo $data.PHP_EOL;
echo $decode.PHP_EOL;

?>

输出内容:

shellcnJ+iB7c/QEApxhoeQm1ZcZmX1z/d7UC1tQtnJD7Dk38io1fHY85My7I/tQPjI3b

0123456789abcdefghijklmnopqrstuvwxyz
0123456789abcdefghijklmnopqrstuvwxyz

CBC模式(密码分组链接:Cipher-block chaining)

CBC模式对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密第一个明文块初始向量数据块异或
这里的初始向量第二重保障,让开头的16字节(128位加密方式)初始向量异或具体看下面的栗子。

这里正好演示一下flag,手动补齐0x00之类的。

<?php
function PadZero($data, $blocksize = 16)
{
	$pad = $blocksize - (strlen($data) % $blocksize);
	return $data . str_repeat("\0", $pad);
}
	
$data = '0123456789abcdefghijklmnopqrstuvwxyz';
$key = "0123456789abcdef";
$iv = '8NONwyJtHesysWpM';
$iv2 = 'gergergertqqwerw';

$encode = base64_encode(openssl_encrypt(PadZero($data),"AES-128-CBC",$key,OPENSSL_NO_PADDING,$iv));
//OPENSSL_ZERO_PADDING自动填充,由于php的问题,还是需要手动补齐,但是返回的直接是base64编码
$encode2 = openssl_encrypt(PadZero($data),"AES-128-CBC",$key,OPENSSL_ZERO_PADDING,$iv);

// 解密
$decode = openssl_decrypt(base64_decode($encode),"AES-128-CBC",$key,OPENSSL_NO_PADDING,$iv2);

//解密还是需要OPENSSL_NO_PADDING
$decode2 = openssl_decrypt(base64_decode($encode2),"AES-128-CBC",$key,OPENSSL_NO_PADDING,$iv2);
echo $encode.PHP_EOL;
echo $encode2.PHP_EOL;
echo $data.PHP_EOL;
echo $decode.PHP_EOL;
echo $decode2.PHP_EOL;
?>

输出内容:
最后那十二个方框实际上是0x00,因为我们加密时补齐了0,我只是写出来让你们看的更明白。

k2GXq52ijs28l14F1JOBFDB2YiEX0BmKpES8bsce9JXpL4FtfHsYldzVt1w3UbSb
k2GXq52ijs28l14F1JOBFDB2YiEX0BmKpES8bsce9JXpL4FtfHsYldzVt1w3UbSb
0123456789abcdefghijklmnopqrstuvwxyz
o&>&(cjgVg\ghijklmnopqrstuvwxyz□□□□□□□□□□□□
o&>&(cjgVg\ghijklmnopqrstuvwxyz□□□□□□□□□□□□

结论,CBC的初始向量只影响数据头部16字节(128位加密方式),所以也没有特别安全。只是加密过程更安全,但是在得知密钥的情况下,IV初始向量作用不大

CFB模式(密文反馈:Cipher feedback)

将前一段加密得到的密文再加密,将第一步加密得到的数据与当前段的明文异或。不要求与16字节(128位加密方式)对齐

<?php
	
$data = '0123456789abcdefghijklmnopqrstuvwxyz';
$key = "0123456789abcdef";
$iv = '8NONwyJtHesysWpM';
$iv2 = 'gergergertqqwerw';

$encode = base64_encode(openssl_encrypt($data,"AES-128-CFB",$key,OPENSSL_RAW_DATA,$iv));


// 解密
$decode = openssl_decrypt(base64_decode($encode),"AES-128-CFB",$key,OPENSSL_RAW_DATA,$iv2);

echo $encode.PHP_EOL;
echo $data.PHP_EOL;
echo $decode.PHP_EOL;

?>

输出内容:

5MuwKWvcGaXaKfenTlK9IX2GpGr3AtFW9qPB8L0ZtOwZ6lPm
0123456789abcdefghijklmnopqrstuvwxyz
KB�����Ǻ�w	~&@ghijklmnopqrstuvwxyz

结论,CFB的初始向量只影响数据头部16字节(128位加密方式),所以同样没有特别安全。同样在得知密钥的情况下,IV初始向量作用不大

OFB模式(输出反馈:Output feedback)

OFB是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作对称性所以加密解密流程一样

<?php
$data = '0123456789abcdefghijklmnopqrstuvwxyz';
$key = "0123456789abcdef";
$iv = '8NONwyJtHesysWpM';
$iv2 = 'gergergertqqwerw';

$encode = base64_encode(openssl_encrypt($data,"AES-128-OFB",$key,OPENSSL_RAW_DATA,$iv));

// 解密
$decode = openssl_decrypt(base64_decode($encode),"AES-128-OFB",$key,OPENSSL_RAW_DATA,$iv2);

echo $encode.PHP_EOL;
echo $data.PHP_EOL;
echo $decode.PHP_EOL;
?>

输出内容:

shell5MuwKWvcGaXaKfenTlK9Ibkd7J6mqD8+YTkratN3B5RNyZfF
0123456789abcdefghijklmnopqrstuvwxyz
KB�����Ǻ�w	~&@b)��2A��8]�cȐQ�A

结论,OFB的初始向量影响整个数据,是因为他的加密机制,利用密钥流与明文流整体异或,哪怕初始向量只有一个字节不同,结果都会差很多。

CTR模式 (计数器:Counter)

它和OFB有些类似,但是有一个counter计数器,每次加密就+1,用这个值使用加密器加密,然后与明文分组异或

<?php
$data = '0123456789abcdefghijklmnopqrstuvwxyz';
$key = "0123456789abcdef";
$iv = '8NONwyJtHesysWpM';
$iv2 = 'gergergertqqwerw';

$encode = base64_encode(openssl_encrypt($data,"AES-128-CTR",$key,OPENSSL_RAW_DATA,$iv));

// 解密
$decode = openssl_decrypt(base64_decode($encode),"AES-128-CTR",$key,OPENSSL_RAW_DATA,$iv2);

echo $encode.PHP_EOL;
echo $data.PHP_EOL;
echo $decode.PHP_EOL;
?>

输出内容:

5MuwKWvcGaXaKfenTlK9IVbSSYBHiHol+Ye3qJBeRdPgfY8h
0123456789abcdefghijklmnopqrstuvwxyz
�Z��Iڱ9���aUX�w鳴��I�(k�iZ&[�

结论,CTR的初始向量影响整个数据,也是因为他的加密机制,哪怕初始向量只有一个字节不同,结果都会差很多。