【问题标题】:How to Decrypt AES SubtleCrypto Web API at SJCL Library如何在 SJCL 库中解密 AES SubtleCrypto Web API
【发布时间】:2022-01-16 03:54:06
【问题描述】:

我们有一个使用加密的 Expo React Native 项目。我们当前的加密基于 SubtleCrypto / Web API [window.subtle.crypto],使用 AES-GCM 128,现在我们需要使用一个在所有平台 [Web、iOS 和 Android] 上普遍可用的库,来自我之前的问题,我们找到了支持 GCM 模式的 SJCL,我们可以完全替换所有基于 Web 的代码,但挑战是我们需要确保所有当前加密的数据也在这个新库中解密,我们必须做到所以:

window.crypto.subtle.encrypt [AES-GCM 128] => (a) ---> SJCL.mode.gcm.decrypt(a)

一旦我们能够成功地做到这一点,我们就可以完全替换库并获得通用平台支持以及向后兼容性。

这意味着我们不能改变处理加密的方式,因为这是要求,我们完全按照下面的代码对其进行加密。

我收到了 Neneil94 的 a very good lead here,但我仍然面临编码/格式方面的问题;这是当前代码:

function arrayBufferToString(buffer){
    var str = "";
    for (var iii = 0; iii < buffer.byteLength; iii++){
        str += String.fromCharCode(buffer[iii]);
    }
    return str;
}

const generateKey = async () =>
{
    const key = await window.crypto.subtle.generateKey(
    {
        name: "AES-GCM",
        length: 128
    }, true, ["encrypt", "decrypt"]);
    const key_exported = await window.crypto.subtle.exportKey("jwk", key);
    return key_exported.k;
}

const text = "This is an encrypted message";
const printCurrent = async () =>
{
    let kkey = await generateKey();
    await window.crypto.subtle.importKey(
        "jwk",
        {
            k: kkey,
            alg: "A128GCM",
            ext: true,
            key_ops: ["encrypt", "decrypt"],
            kty: "oct",
        },
        {
            name: "AES-GCM",
            length: 128
        },
        false,
        ["encrypt", "decrypt"]
    ).then(function(key)
    {
        window.crypto.subtle.encrypt(
        {
            name: "AES-GCM",
            iv: new Uint8Array(12)
        }, key, new TextEncoder().encode(JSON.stringify(text))).then(function(encryptedObject)
        {
            console.log({kkey}); //{kkey: 'eKM_Cen2Z-jhedM284cltA'}

            let bkey = sjcl.codec.utf8String.toBits(kkey);
            console.log({bkey}); //{bkey: Array(6)}
            let cipher = new sjcl.cipher.aes(bkey);
            //let bdata = sjcl.codec.base64.toBits(encryptedObject); //gives error of a.replace
            let bdata = arrayBufferToString(encryptedObject);
            let ivvv = new Uint8Array(12);
            let ivv = Buffer.from(ivvv).toString('base64');
            let ive = sjcl.codec.base64.toBits(ivv);

            // decrypt
            let decbits = sjcl.mode.gcm.decrypt(cipher, bdata, ive);

            // convert into utf8string
            decryptedData = sjcl.codec.utf8String.fromBits(decbits);
        });
    });
}

我得到的错误是:

损坏:gcm:标签不匹配

我不确定上面链接的 Neneil 代码中的 aesKey 究竟是什么,它是 CryptoKey 对象还是从 JsonWebKey 生成的 22 个字符?对于“encryptedObject”,我如何将其转换为 BitArray。对于 IV,我只是提供 Encrypt 正在做的事情,但我也不确定..

不过,任何见解都会很有帮助,感谢您的宝贵时间!

【问题讨论】:

    标签: javascript react-native cryptography expo aes


    【解决方案1】:

    你的代码有两个问题:

    • kkey 是 Base64url 编码的原始密钥。这必须首先转换为 Base64,然后再转换为 bitArray
    let kkeyB64 = kkey.replace(/-/g, '+').replace(/_/g, '/');   // Base64url -> Base64 (ignore optional padding)
    let bkey = sjcl.codec.base64.toBits(kkeyB64);               // conert to bitArray
    
    • 密文和 IV 以 ArrayBuffer 形式给出,因此使用 ArrayBuffer codec 是有意义的,例如:
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/sjcl@1.0.8/core/codecArrayBuffer.js"></script>
    
    let bdata = sjcl.codec.arrayBuffer.toBits(encryptedObject)
    let ive = sjcl.codec.arrayBuffer.toBits(new Uint8Array(12).buffer)
    

    通过这些更改,使用 sjcl 解密成功。


    完整代码:

    const generateKey = async () =>
    {
        const key = await window.crypto.subtle.generateKey(
        {
            name: "AES-GCM",
            length: 128
        }, true, ["encrypt", "decrypt"]);
        const key_exported = await window.crypto.subtle.exportKey("jwk", key);
        return key_exported.k;
    }
    
    const text = "This is an encrypted message";
    const printCurrent = async () =>
    {
        let kkey = await generateKey();
        await window.crypto.subtle.importKey(
            "jwk",
            {
                k: kkey,
                alg: "A128GCM",
                ext: true,
                key_ops: ["encrypt", "decrypt"],
                kty: "oct",
            },
            {
                name: "AES-GCM",
                length: 128
            },
            false,
            ["encrypt", "decrypt"]
        ).then(function(key)
        {
            window.crypto.subtle.encrypt(
            {
                name: "AES-GCM",
                iv: new Uint8Array(12)
            }, key, new TextEncoder().encode(JSON.stringify(text))).then(function(encryptedObject)
            {
                //console.log({kkey}); //{kkey: 'eKM_Cen2Z-jhedM284cltA'}
    
                let kkeyB64 = kkey.replace(/-/g, '+').replace(/_/g, '/'); // Fix 1
                let bkey = sjcl.codec.base64.toBits(kkeyB64); 
                //console.log({bkey}); //{bkey: Array(6)}
                let cipher = new sjcl.cipher.aes(bkey);
                
                
                let bdata = sjcl.codec.arrayBuffer.toBits(encryptedObject) // Fix 2
                let ive = sjcl.codec.arrayBuffer.toBits(new Uint8Array(12).buffer) // Fix 3
                
                // decrypt
                let decbits = sjcl.mode.gcm.decrypt(cipher, bdata, ive);
    
                // convert into utf8string
                decryptedData = sjcl.codec.utf8String.fromBits(decbits);
                document.getElementById("pt").innerHTML = JSON.parse(decryptedData);
            });
        });
    }
    
    printCurrent();
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/sjcl@1.0.8/sjcl.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/sjcl@1.0.8/core/codecArrayBuffer.js"></script>
    <p style="font-family:'Courier New', monospace;" id="pt"></p>

    如果 ArrayBuffer 编解码器 由于某种原因在您的环境中不可用(我不确定),那么 ArrayBuffer 也可以是显式的十六进制或 Base64编码后转成bitArray,例如:

    // https://stackoverflow.com/a/40031979/9014097
    function buf2hex(buffer) { 
        return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
    }
    ...
    let bdata = sjcl.codec.hex.toBits(buf2hex(encryptedObject)) 
    let ive = sjcl.codec.hex.toBits(buf2hex(new Uint8Array(12).buffer)) 
    

    安全性:
    静态IV(如new Uint8Array(12))是GCM 上下文中的致命错误,s。例如here。而是为 each 加密生成一个 random IV,它与密文(通常是连接的)一起传递。因此,除非静态 IV 仅用于测试目的,否则必须修复此问题。

    【讨论】:

    • ArrayBuffer 在我的环境中确实不可用,但您甚至有解决方案,您是圣人!完美运行,非常感谢!感谢您还指出静态 IV 安全漏洞,我们应该修复它,因为它不仅用于测试。干杯!
    猜你喜欢
    • 1970-01-01
    • 2013-04-08
    • 1970-01-01
    • 2015-12-16
    • 1970-01-01
    • 1970-01-01
    • 2016-07-28
    • 1970-01-01
    • 2020-05-24
    相关资源
    最近更新 更多