简体   繁体   English

phpseclib AES-GCM 加密是否与 javascript WebCrypto 兼容?

[英]Is phpseclib AES-GCM encryption compatible with javascript WebCrypto?

I'm trying to encrypt/decrypt symmetrically from php/phpseclib to js/WebCrypto (SubtleCrypto).我正在尝试从php/phpseclib对称加密/解密到js/WebCrypto (SubtleCrypto)。 The algorithm is AES-GCM with PBKDF2 derivation and also with plain key.该算法是带有 PBKDF2 派生的AES-GCM ,也带有明文密钥。 I had no success.我没有成功。 The error received from the window.crypto.subtle.decrypt() function is OperationError: The operation failed for an operation-specific reason .从 window.crypto.subtle.decrypt() function 收到的错误是OperationError: The operation failed for an operation-specific reason RSA-OAEP works without any problems. RSA-OAEP 没有任何问题。

Did anybody do this before - is it possible at all?以前有没有人这样做过 - 有可能吗? I didn't find anything that confirms or denies a compatibility between these modules.我没有找到任何可以确认或否认这些模块之间兼容性的东西。

Edit: adding code example编辑:添加代码示例

encryption:加密:

<?php
require_once($_SERVER['DOCUMENT_ROOT'] . '/../vendor/autoload.php');
use phpseclib3\Crypt\AES;

$TEST_AES_KEY = "TWw4QCkeZEnXoCDkI1GEHQ==";
$TEST_AES_IV = "CRKTyQoWdWB2n56f";
$message = "123&abc";

$aes = new AES('gcm');
$aes->setKey($TEST_AES_KEY);
$aes->setNonce($TEST_AES_IV);

$ciphertext = $aes->encrypt($message);
$tag = $aes->getTag();
$ciphertextBase64 = base64_encode($ciphertext . $tag);
echo $ciphertextBase64;

decryption:解密:

<!DOCTYPE html>
<html>
<script>
    function _base64ToArrayBuffer(base64) {
        var binary_string   = atob(base64);
        var len             = binary_string.length;
        var bytes           = new Uint8Array(len);
        for (var i = 0; i < len; i++) {
            bytes[i] = binary_string.charCodeAt(i);
        }
        return bytes.buffer;
    }

    async function _importKeyAes(key) {
        return await window.crypto.subtle.importKey(
            "raw",
            key,
            { name: "AES-GCM" },
            false,
            ["encrypt", "decrypt"]
        );
    }

    async function decryptMessageSymetric(key, data, iv) {
        keyArrayBuffer  = _base64ToArrayBuffer(key);
        key             = await _importKeyAes(keyArrayBuffer);
        iv              = _base64ToArrayBuffer(iv);
        data            = _base64ToArrayBuffer(data);
        result = await window.crypto.subtle.decrypt(
            { name: "AES-GCM", iv: iv, tagLength: 128 },
            key,
            data
        );
        return new TextDecoder().decode(result);
    }

    TEST_AES_KEY = "TWw4QCkeZEnXoCDkI1GEHQ==";
    TEST_AES_IV = "CRKTyQoWdWB2n56f";
    messageEncrypted = "LATYboD/FztIKGVkiJNWHOP72C77FiY="; // result from phpseclib encryption

    result = decryptMessageSymetric(TEST_AES_KEY, messageEncrypted, TEST_AES_IV);
    console.log(result);
</script>
</html>

There are only two minor encoding bugs:只有两个小的编码错误:

  • In the phpseclib code the key is not Base64 encoded, in the WebCrypto code it is Base64 encoded.在 phpseclib 代码中,密钥不是 Base64 编码的,在 WebCrypto 代码中它是 Base64 编码的。 This needs to be changed so that both sides use the same key.这需要更改,以便双方使用相同的密钥。
    For the test below I arbitrarily decide to use the WebCrypto solution, ie in the phpseclib code a Base64 encoding is added:对于下面的测试,我任意决定使用 WebCrypto 解决方案,即在 phpseclib 代码中添加了 Base64 编码:

     $TEST_AES_KEY = base64_decode("TWw4QCkeZEnXoCDkI1GEHQ==");

    This produces a 16 bytes key so that AES-128 is applied (note that the phpseclib solution would also be possible, since the Base64 encoded key is 24 bytes in size and corresponds to AES-192; no matter which key is applied in the end, the important thing is that on both sides the same key must be used).这会产生一个 16 字节的密钥,以便应用 AES-128(请注意,phpseclib 解决方案也是可能的,因为 Base64 编码的密钥大小为 24 字节并且对应于 AES-192;无论最后应用哪个密钥,重要的是双方必须使用相同的密钥)。
    Running the phpseclib code again gives the following ciphertext:再次运行 phpseclib 代码会得到以下密文:

     7K+HAB7Ch9V4jJ1XJPM0sANXA2ocJok=

    In the WebCrypto code, this new ciphertext is now used.在 WebCrypto 代码中,现在使用了这个新的密文。

  • In the WebCrypto code the 16 bytes IV is Base64 decoded.在 WebCrypto 代码中,16 字节 IV 被 Base64 解码。 This creates an IV that is too short for AES.这会创建一个对于 AES 来说太短的 IV。 Therefore the Base64 decoding is removed and a UTF-8 encoding (analogous to the phpseclib code) is performed:因此删除了 Base64 解码并执行 UTF-8 编码(类似于 phpseclib 代码):

     iv = new TextEncoder().encode(iv);

With these changes decryption is successful:通过这些更改解密成功:

 (async () => { function _base64ToArrayBuffer(base64) { var binary_string = atob(base64); var len = binary_string.length; var bytes = new Uint8Array(len); for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); } return bytes.buffer; } async function _importKeyAes(key) { return await window.crypto.subtle.importKey( "raw", key, { name: "AES-GCM" }, false, ["encrypt", "decrypt"] ); } async function decryptMessageSymetric(key, data, iv) { keyArrayBuffer = _base64ToArrayBuffer(key); key = await _importKeyAes(keyArrayBuffer); iv = new TextEncoder().encode(iv); // Remove Base64 decoding data = _base64ToArrayBuffer(data); result = await window.crypto.subtle.decrypt( { name: "AES-GCM", iv: iv, tagLength: 128 }, key, data ); return new TextDecoder().decode(result); } TEST_AES_KEY = "TWw4QCkeZEnXoCDkI1GEHQ=="; TEST_AES_IV = "CRKTyQoWdWB2n56f"; messageEncrypted = "7K+HAB7Ch9V4jJ1XJPM0sANXA2ocJok="; // Apply modified ciphertext result = await decryptMessageSymetric(TEST_AES_KEY, messageEncrypted, TEST_AES_IV); console.log(result); })();


Note that a static IV is a serious security risk for GCM, s.请注意,static IV 对 GCM 来说是一个严重的安全风险,s。 here .在这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM