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字節密鑰既不會令人意外,也不會失敗。但是,那裏是:(。 –