理解PHP的AES加密

这是一篇翻译的文章,原文在这里。为排版效果,有改动。

理解PHP的AES加密

这一段PHP代码给出了PHP的AES加密的一个基本轮廓

首先要了解的东西是下面几个常量:

MCRYPT_RIJNDAEL_128
MCRYPT_RIJNDAEL_192
MCRYPT_RIJNDAEL_256

你可能认为MCRYPT_RIJNDAEL_256意味着256位加密技术,但其实不是的。这三个选项定义的是Rijndael加密过程中所使用的块尺寸(block-size),而与密钥长度(即所谓的加密强度)完全无关。(后面会解释AES加密中的加密强度如何设置。)

Rijndael是一种块加密算法,它在一系列互不相关的数据块上分别进行操作。所以必须对数据进行补位操作,以保证被加密的数据长度正好是块尺寸的整数倍。(PHP使用NULL字节进行补位) 因此,如果你指定了MCRYPT_RIJNDAEL_256,你加密输出的数据长度一定是32字节(256位)的整数倍;如果你指定了MCRYPT_RIJNDAEL_128,你的加密输出则一定是16字节的整数倍。

注意:严格地说,AES和Rijndael并不完全等价(尽管实践中它们经常互换使用),因为Rijndael支持更大范围的块尺寸和密钥长度;AES使用固定的128位块尺寸,而密钥长度可以是128位、192位或者256位。Rijndael的块尺寸和密钥长度只要求使用32位的整数倍,最小128位,最大256位。

简而言之:如果你想和AES兼容,请永远使用MCRYPT_RIJNDAEL_128。

那么,第一步自然是初始化加密器对象

$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');

我们使用CBC模式(cipher-block chaining)进行加密。块加密模式详见这里。要使用CBC模式,需要提供一个初始向量(IV)。初始向量的长度必须和块尺寸相当(不一定等于密钥长度)。既然我们的块尺寸是128位,所以IV也应该是128位(16字节)。也就是说,对于AES加密方法,初始向量总是16字节,不论加密强度如何。

这里是验证初始向量长度的代码:

$iv_size = mcrypt_enc_get_iv_size($cipher);
printf("iv_size = %d\n",$iv_size);

那么在PHP中,如何进行256位而不是128位的AES加密?答案是使用32字节长的密钥。

譬如:

$key256 = '12345678901234561234567890123456';
$key128 = '1234567890123456';

我们的128位初始向量既可以用于128位加密,又可以用于256位加密。

$iv =  '1234567890123456';

printf("iv: %s\n",bin2hex($iv));
printf("key256: %s\n",bin2hex($key256));
printf("key128: %s\n",bin2hex($key128));

这是要加密的原文:

$cleartext = 'The quick brown fox jumped over the lazy dog';
printf("plainText: %s\n\n",$cleartext);

mcrypt_generic_init函数初始化的时候,需要同时提供密钥和初始向量。密钥的长度决定了我们是在进行128位、192位还是256位加密。这里我们先进行256位加密:

if (mcrypt_generic_init($cipher, $key256, $iv) != -1)
{
    // 如果$cleartext的长度并非恰好是块尺寸的整数倍,PHP会用NULL字符进行补位
    $cipherText = mcrypt_generic($cipher,$cleartext );
    mcrypt_generic_deinit($cipher);

    // 以十六进制显示最终结果:
    printf("256-bit encrypted result:\n%s\n\n",bin2hex($cipherText));
}

这次我们进行128位加密:

if (mcrypt_generic_init($cipher, $key128, $iv) != -1)
{
    // 如果$cleartext的长度并非恰好是块尺寸的整数倍,PHP会用NULL字符进行补位
    $cipherText = mcrypt_generic($cipher,$cleartext );
    mcrypt_generic_deinit($cipher);

    // 以十六进制显示最终结果:
    printf("128-bit encrypted result:\n%s\n\n",bin2hex($cipherText));
}

结果

你可以用下列测试来指导针对你自己的AES实现进行的测试

256位密钥CBC模式

IV = '1234567890123456'
 (hex: 31323334353637383930313233343536)
Key = '12345678901234561234567890123456'
 (hex: 3132333435363738393031323334353631323334353637383930313233343536)
PlainText:
 'The quick brown fox jumped over the lazy dog'
CipherText(hex):
 2fddc3abec692e1572d9b7d629172a05caf230bc7c8fd2d26ccfd65f9c54526984f7cb1c4326ef058cd7bee3967299e3

128位密钥CBC模式

IV = '1234567890123456'
 (hex: 31323334353637383930313233343536)
Key = '1234567890123456'
 (hex: 31323334353637383930313233343536)
PlainText:
 'The quick brown fox jumped over the lazy dog'
CipherText(hex):
 f78176ae8dfe84578529208d30f446bbb29a64dc388b5c0b63140a4f316b3f341fe7d3b1a3cc5113c81ef8dd714a1c99

标签: aes, php, crypto

仅有一条评论

  1. 水上木 水上木

    谢谢分享,一直搞不懂aes256加密算法,看了文章终于明白了!

添加新评论