在CryptoJS和PHP中为AES加密从字符串派生密钥和IV

时间:2015-04-22 19:29:25

标签: php hash cryptography cryptojs

如何从公共字符串生成密钥和IV,它们可以使用PHP中的AES在加密算法中提供?

示例:

$key_string = derivate_in_valid_key("i love stackoverflow");
$iv_string = derivate__in_valid_iv("i love questions");

并且我可以在 JavaScript 中重复推导过程。

1 个答案:

答案 0 :(得分:9)

您可以使用PBKDF2从密码中导出密钥和IV。 CryptoJSPHP都提供了其实现。

AES支持密钥大小为128,192和256位,块大小为128位。 IV必须与CBC等模式的块大小相同。

单次调用PBKDF2以仅生成密钥

以下代码也适用于生成IV。这样做需要第二个随机盐,必须与密文一起发送。

JavaScript(DEMO):

var password = "test";

var iterations = 500;
var keySize = 256;
var salt = CryptoJS.lib.WordArray.random(128/8);

console.log(salt.toString(CryptoJS.enc.Base64));

var output = CryptoJS.PBKDF2(password, salt, {
    keySize: keySize/32,
    iterations: iterations
});

console.log(output.toString(CryptoJS.enc.Base64));

示例输出:

CgxEDCi5z4ju1ycmKRh6aw==  
7G3+NUWtbOooVeTDyLqMaDgnqCkiQCjZi3wnspRPabU=

PHP:

$password = "test";
$expected = "7G3+NUWtbOooVeTDyLqMaDgnqCkiQCjZi3wnspRPabU=";

$salt = 'CgxEDCi5z4ju1ycmKRh6aw==';
$hasher = "sha1"; // CryptoJS uses SHA1 by default
$iterations = 500;
$outsize = 256;

$out = hash_pbkdf2($hasher, $password, base64_decode($salt), $iterations, $outsize/8, true);

echo "expected: ".$expected."\ngot:      ".base64_encode($out);

输出:

expected: 7G3+NUWtbOooVeTDyLqMaDgnqCkiQCjZi3wnspRPabU=
got:      7G3+NUWtbOooVeTDyLqMaDgnqCkiQCjZi3wnspRPabU=

单次调用PBKDF2以生成密钥 IV

上一节有点笨重,因为需要生成两个盐并进行两次PBKDF2调用。 PBKDF2支持可变输出,因此可以简单地使用一个盐,请求密钥大小加上iv大小的输出并将其切掉。以下代码执行此操作,因此只有一个salt必须与密文一起发送。

JavaScript(DEMO):

var password = "test";

var iterations = 1000;
// sizes must be a multiple of 32
var keySize = 256;
var ivSize = 128;
var salt = CryptoJS.lib.WordArray.random(128/8);

console.log(salt.toString(CryptoJS.enc.Base64));

var output = CryptoJS.PBKDF2(password, salt, {
    keySize: (keySize+ivSize)/32,
    iterations: iterations
});

// the underlying words arrays might have more content than was asked: remove insignificant words
output.clamp();

// split key and IV
var key = CryptoJS.lib.WordArray.create(output.words.slice(0, keySize/32));
var iv = CryptoJS.lib.WordArray.create(output.words.slice(keySize/32));

console.log(key.toString(CryptoJS.enc.Base64));
console.log(iv.toString(CryptoJS.enc.Base64));

示例输出:

0Iulef2TncciKGmdwvQX3Q==
QeTc3zHuG3JcdtOCkzU2uJWTnrMEggvF1dNUbgNMyzg=
L1YNlFe54+Cvepp/pXsHtg==

PHP:

$password = "test";
$expectedKey = "QeTc3zHuG3JcdtOCkzU2uJWTnrMEggvF1dNUbgNMyzg=";
$expectedIV = "L1YNlFe54+Cvepp/pXsHtg==";

$salt = '0Iulef2TncciKGmdwvQX3Q==';
$hasher = "sha1";
$iterations = 1000;
$keysize = 256;
$ivsize = 128;

$out = hash_pbkdf2($hasher, $password, base64_decode($salt), $iterations, ($keysize+$ivsize)/8, true);

// split key and IV
$key = substr($out, 0, $keysize/8);
$iv = substr($out, $keysize/8, $ivsize/8);

// print for demonstration purposes
echo "expected key: ".$expectedKey."\ngot:          ".base64_encode($key);
echo "\nexpected iv: ".$expectedIV."\ngot:         ".base64_encode($iv);

输出:

expected key: QeTc3zHuG3JcdtOCkzU2uJWTnrMEggvF1dNUbgNMyzg=
got:          QeTc3zHuG3JcdtOCkzU2uJWTnrMEggvF1dNUbgNMyzg=
expected iv: L1YNlFe54+Cvepp/pXsHtg==
got:         L1YNlFe54+Cvepp/pXsHtg==

CryptoJS默认使用SHA1,因此您可以通过将哈希函数作为hasher对象属性传递来使用不同的哈希函数。您还应该使用更高的迭代次数。

我没有PHP 5.5+版本,所以我使用了here的PBKDF2实现。