【问题标题】:Trouble decrypting openSSL AES CTR encrypted text解密openSSL AES CTR加密文本时遇到问题
【发布时间】:2017-01-21 20:13:56
【问题描述】:

我无法解密使用openssl_encrypt 方法在php 中加密的消息。我正在使用新的 WebCrypto API(所以我使用 crypto.subtle)。

在 php 中加密:

$ALGO = "aes-256-ctr";

$key = "ae6865183f6f50deb68c3e8eafbede0b33f9e02961770ea5064f209f3bf156b4";

function encrypt ($data, $key) {
    global $ALGO;

    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($ALGO), $strong);
    if (!$strong) {
        exit("can't generate strong IV");
    }

    return bin2hex($iv).openssl_encrypt($data, $ALGO, $key, 0, $iv);
}


$enc = encrypt("Lorem ipsum dolor", $key);

exit($enc);

示例输出:

8d8c3a57d2dbb3287aca61be0bce59fbeAQ4ILKouAQ5eizPtlUTeHU=

(我可以在 php 中解密并取回明文)

在 JS 中我这样解密:

function Ui8FromStr (StrStart) {
    const Ui8Result = new Uint8Array(StrStart.length);

    for (let i = 0; i < StrStart.length; i++) {
        Ui8Result[i] = StrStart.charCodeAt(i);
    }

    return Ui8Result;
}

function StrFromUi8 (Ui8Start) {
    let StrResult = "";

    Ui8Start.forEach((charcode) => {
        StrResult +=    String.fromCharCode(charcode);
    });

    return StrResult;
}

function Ui8FromHex (hex) {
    for (var bytes = new Uint8Array(Math.ceil(hex.length / 2)), c = 0; c < hex.length; c += 2)
        bytes[c/2] = parseInt(hex.substr(c, 2), 16);
    return bytes;
}

const ALGO = 'AES-CTR'
function decrypt (CompCipher, HexKey) {
    return new Promise (function (resolve, reject) {
        // remove IV from cipher
        let HexIv = CompCipher.substr(0, 32);
        let B64cipher = CompCipher.substr(32);

        let Ui8Cipher = Ui8FromStr(atob(B64cipher));

        let Ui8Iv = Ui8FromHex (HexIv);
        let Ui8Key = Ui8FromHex (HexKey);

        crypto.subtle.importKey("raw", Ui8Key, {name: ALGO}, false, ["encrypt", "decrypt"]). then (function (cryptokey){

            return crypto.subtle.decrypt({ name: ALGO, counter: Ui8Iv, length: 128}, cryptokey, Ui8Cipher).then(function(result){
                let Ui8Result = new Uint8Array(result);
                let StrResult =  StrFromUi8(Ui8Result);
                resolve(StrResult);

            }).catch (function (err){

                reject(err)

            }); 
        })
    })
}

当我现在运行 decrypt("8d8c3a57d2dbb3287aca61be0bce59fbeAQ4ILKouAQ5eizPtlUTeHU=", "ae6865183f6f50deb68c3e8eafbede0b33f9e02961770ea5064f209f3bf156b4").then(console.log) 时,我会胡言乱语:SÌõŰblfçSÑ-

我的问题是,我不确定counter 是什么意思。我尝试了静脉注射但失败了。

This Github tutorial 建议*1,它是 IV - 或者至少是它的一部分,因为我看到人们谈论计数器是 IV 的一部分(类似于 4字节,这意味着 IV 由 12 个字节的 IV 和 4 个字节的计数器组成)

如果确实如此,那么我的问题就变成了:当计数器只有 4 个字节时,我应该在哪里给脚本其他 12 个字节的 IV。

谁能给我一个在php中加密的工作示例

*1 它表示必须使用相同的计数器进行加密和解密。这让我相信,它至少类似于 IV

【问题讨论】:

  • @zaph 输出由 32 个 hex IV 字符(16 个字节)+ 24 个 Base64 密文字符(17 个字节)密文总数 = 16 + 17 = 33
  • @zaph 是什么让你相信密文是 24 字节?正如我所说,它前面有 IV,但 IV 是十六进制的。因此,密文只有 17 个以 64 为基数的字节(前置 16 个字节的十六进制 - 即前置 32 个字符的十六进制)
  • 您所拥有的是对加密数据进行 IV 和 Base64 编码的十六进制编码的错误混搭。这是一个非常糟糕的主意,它确实让我感到困惑并且肯定失败了"Rule of Least Surprise",在界面设计中,总是做最不令人惊讶的事情。

标签: javascript encryption cryptography php-openssl webcrypto-api


【解决方案1】:

您在 PHP 中错误地处理了密钥。

在 PHP 代码中,您将十六进制编码的密钥直接传递给 openssl_encrypt 函数,而不对其进行解码。这意味着您尝试使用的密钥是预期的两倍(即 64 字节)。然而,OpenSSL 不检查密钥长度——它只是截断它,取前 32 个字节并将它们用作加密密钥。

Javascript 代码正确处理密钥,在将解码后的数组传递给解密函数之前对其进行十六进制解码。

总体结果是您在每种情况下都使用不同的密钥,因此解密不起作用。

您需要在 PHP 代码中的键上添加对 hex2bin 的调用,以将其从十六进制编码转换为实际的 32 个原始字节。

【讨论】:

  • 您可以肯定地说,为 AES 接受 64 字节密钥既不符合最小意外也不符合快速失败原则。但是,是的,就是这样:(。
猜你喜欢
  • 2020-01-20
  • 2015-06-09
  • 2014-08-05
  • 2018-11-06
  • 2017-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-09
相关资源
最近更新 更多