javascript - JavaScript加密,PHP解密

标签 javascript php encryption mcrypt cryptojs

我正在用 JavaScript 加密我的用户密码,如下所示:

 var encryptedPassword = CryptoJS.AES.encrypt(password, "Secret Passphrase");

它工作正常,但现在我正尝试在服务器端用 PHP 解密,如下所示:

 $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
 $decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, "Secret Passphrase", base64_decode($password), MCRYPT_MODE_CBC, $iv);

根本不起作用,解密后的密码字符串看起来很奇怪:

 string(64) ">�OX2MS��댗v�<$�ʕ��i�̄��_��P���\�կ=�_6(�m����,4WT7��a"

这是我在 JavaScript 中的代码在有用的评论之后的当前状态:

    var encryptedPassword = CryptoJS.AES.encrypt(password, "Secret Passphrase");
    var ivHex = encryptedPassword.iv.toString();
    var ivSize = encryptedPassword.algorithm.ivSize; // same as blockSize
    var keySize = encryptedPassword.algorithm.keySize;
    var keyHex = encryptedPassword.key.toString();
    var saltHex = encryptedPassword.salt.toString(); // must be sent
    var openSslFormattedCipherTextString = encryptedPassword.toString(); // not used
    var cipherTextHex = encryptedPassword.ciphertext.toString(); // must be sent

我将 saltHex 和 CipherTextHex 发送到 PHP 服务器,我正在使用 mcrypt_decrypt() ,如下所示:

 $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), $saltHex);
 $decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, "Secret Passphrase", base64_decode($cipherTextHex), MCRYPT_MODE_CBC, $iv);

它仍然不适用于此更新后的代码。

有人可以帮助我使用 mcrypt_decrypt() PHP 函数正确解密一个简单的 AES 加密方法吗?我确定我的 mcrypt_decrypt() 方法中的密码、mcrypt 模式和 IV 参数有问题。如果你知道,谢谢。

最佳答案

问题在于,在 CryptoJS 代码中,密码用于派生 key 和用于 AES 加密的 IV,但 mcrypt 仅使用 key 来加密/解密。此信息需要传递给 php。由于不想传递密码,所以必须在php中以同样的方式推导出 key 和IV。

以下代码从密码和盐派生出 key 和 IV。它是根据我的答案 here 中的代码建模的(了解更多信息)。

function evpKDF($password, $salt, $keySize = 8, $ivSize = 4, $iterations = 1, $hashAlgorithm = "md5") {
    $targetKeySize = $keySize + $ivSize;
    $derivedBytes = "";
    $numberOfDerivedWords = 0;
    $block = NULL;
    $hasher = hash_init($hashAlgorithm);
    while ($numberOfDerivedWords < $targetKeySize) {
        if ($block != NULL) {
            hash_update($hasher, $block);
        }
        hash_update($hasher, $password);
        hash_update($hasher, $salt);
        $block = hash_final($hasher, TRUE);
        $hasher = hash_init($hashAlgorithm);

        // Iterations
        for ($i = 1; $i < $iterations; $i++) {
            hash_update($hasher, $block);
            $block = hash_final($hasher, TRUE);
            $hasher = hash_init($hashAlgorithm);
        }

        $derivedBytes .= substr($block, 0, min(strlen($block), ($targetKeySize - $numberOfDerivedWords) * 4));

        $numberOfDerivedWords += strlen($block)/4;
    }

    return array(
        "key" => substr($derivedBytes, 0, $keySize * 4),
        "iv"  => substr($derivedBytes, $keySize * 4, $ivSize * 4)
    );
}

salt是在CryptoJS加密的时候生成的,需要和密文一起发给php。在调用 evpKDF 之前,盐必须从十六进制转换为二进制字符串。

$keyAndIV = evpKDF("Secret Passphrase", hex2bin($saltHex));
$decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, 
        $keyAndIV["key"], 
        hex2bin($cipherTextHex), 
        MCRYPT_MODE_CBC, 
        $keyAndIV["iv"]);

如果只向服务器发送了encryptedPassword.toString(),那么在使用前需要将salt和实际密文分开。该格式是专有的 OpenSSL 兼容格式,前 8 个字节是“Salted__”,接下来的 8 个字节是随机盐,其余是实际的密文。一切都经过 Base64 编码。

function decrypt($ciphertext, $password) {
    $ciphertext = base64_decode($ciphertext);
    if (substr($ciphertext, 0, 8) != "Salted__") {
        return false;
    }
    $salt = substr($ciphertext, 8, 8);
    $keyAndIV = evpKDF($password, $salt);
    $decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, 
            $keyAndIV["key"], 
            substr($ciphertext, 16), 
            MCRYPT_MODE_CBC, 
            $keyAndIV["iv"]);

    // unpad (PKCS#7)
    return substr($decryptPassword, 0, strlen($decryptPassword) - ord($decryptPassword[strlen($decryptPassword)-1]));
}

使用 OpenSSL 扩展而不是 Mcrypt 也可以实现同样的效果:

function decrypt($ciphertext, $password) {
    $ciphertext = base64_decode($ciphertext);
    if (substr($ciphertext, 0, 8) != "Salted__") {
        return false;
    }
    $salt = substr($ciphertext, 8, 8);
    $keyAndIV = evpKDF($password, $salt);
    $decryptPassword = openssl_decrypt(
            substr($ciphertext, 16), 
            "aes-256-cbc",
            $keyAndIV["key"], 
            OPENSSL_RAW_DATA, // base64 was already decoded
            $keyAndIV["iv"]);

    return $decryptPassword;
}

关于javascript - JavaScript加密,PHP解密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27677236/

相关文章:

javascript - Ruby 和 JavaScript 之间的 RSA key 对加密/解密?

javascript - 如何在点击处理程序上检查键盘修饰符,如 "shift"?

javascript - 使用 knockout.js 将两个数组相互绑定(bind)

php - 在 php-header 的菜单中突出显示当前页面

php - 制定一个类似 cron 的时间表

javascript - 在 Base64 链接编码上添加目标空白

javascript - Prisma findMany 函数不返回关系数据

javascript - Angular JS Amcharts 指令

php - 更改实时站点(Codeigniter,但不特定于它)

ios - 解码 base64 + 解密 - Swift