1

我有麻烦解密在PHP与openssl_encrypt方法加密的消息。我正在使用新的WebCrypto API(所以我使用crypto.subtle)。麻烦解密OpenSSL的AES CTR加密文本

加密在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。我尝试了IV,但失败了。

This Github tutorial提示* ,它是IV - 或至少它的一部分,因为我已经看到人们谈论计数器是IV的一部分(类似4个字节,这意味着,四是从12个字节IV和4字节计数器)

如果这确实是真的做,那么我的问题是:我在哪里给脚本的其他12个字节IV的柜台时,只有4字节它。

谁能也许给我的加密工作的例子在PHP

* 它说,同样的柜台已被用于烯和解密。这使我相信,这是至少类似于IV

+0

@zaph输出被做出来的十六进制IV的32个字符(16个字节)+为Base64密文的24个字符(17个字节) 密文总= 16 + 17 = 33 –

+0

@zaph什么导致你认为密文是24字节?正如我所说,它已经预先IV,但IV是在十六进制。因此,基数64中的密文仅为17个字节(16字节的十六进制前缀 - 即32个字符的十六进制前缀) –

+0

您拥有的是对IV和Base64编码的加密数据进行十六进制编码的错误混合。这是一个非常糟糕的主意,它肯定会使我困惑,并且肯定会失败[“最少惊奇规则”](http://principles-wiki.net/principles:Principle%20of%20Least%20Surprise?redirect=1),In界面设计,总是做最不令人意外的事情。 – zaph

回答

3

你是不正确地处理关键在PHP的东西。

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

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

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

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

+1

你当然可以说接受一个AES字节的64字节密钥既不会令人意外,也不会失败。但是,那里是:(。 –