我收到“给定密文的错误密钥”

时间:2019-04-05 21:35:28

标签: javascript cryptography webassembly libsodium

(编辑) 仍在此。我更改了代码,现在直接从库的Github页面中使用示例函数,但是仍然存在相同的问题。我在这里错过明显的东西吗?我唯一的偏差是使用内置 crypto_genrichash函数,用于在32个字符(64个字符过长)中创建密钥。使用他们的示例,创建的密钥太小:

var key = sodium.randombytes_buf(sodium.crypto_shorthash_KEYBYTES),
hash1 = sodium.crypto_shorthash(new Uint8Array([1, 2, 3, 4]), key),
hash2 = sodium.crypto_shorthash('user_password', key);

原始钠文件的链接 https://raw.githubusercontent.com/jedisct1/libsodium.js/master/dist/browsers/sodium.js

这是导致错误的函数:

function RA(A, I, e, B) {
            var C = [];
            E(B),
            A = w(C, A, "ciphertext");
            var i, a = g._crypto_secretbox_macbytes(), r = A.length;
            r < a && _(C, "ciphertext is too short"),
            i = s(A),
            C.push(i),
            I = w(C, I, "nonce");
            var t, Q = 0 | g._crypto_secretbox_noncebytes();
            I.length !== Q && _(C, "invalid nonce length"),
            t = s(I),
            C.push(t),
            e = w(C, e, "key");
            var o, h = 0 | g._crypto_secretbox_keybytes();
            e.length !== h && _(C, "invalid key length"),
            o = s(e),
            C.push(o);
            var p = new c(r - g._crypto_secretbox_macbytes() | 0)
              , u = p.address;
            if (C.push(u),
            0 == (0 | g._crypto_secretbox_open_easy(u, i, r, 0, t, o))) {
                var l = n(p, B);
                return y(C),
                l
            }
            f(C, "wrong secret key for the given ciphertext")
        }

(/编辑)
我相信我会尽量减少这段代码以供自己学习,我已经消除了盐分或随机数等式中的某些内容。但是,不管怎么说,我不明白的是,如果按原样运行它,似乎可以正常工作,因为两个控制台日志在加密之前和解密之后都显示相同的privateKey精确值。但是,在实际使用中,加密部分将位于单独的脚本中,例如用户创建/编辑pw页,发送给mysql进行存储(通过ajax / php)的新加密的privateKey以及用户中的解密部分。登录脚本..这是当我在控制台中收到“给定密文的错误密钥”错误时。但是我已经检查过,从表面上看,ajax在身份验证时返回的值(注意:位于带有其他确认的数组中)与解密PrivateKey(result [6],password_normal_input)完全相同。就像第一次创建时一样……至少在视觉上(检查返回和密码输入中的空格)。

和钠包装(带有示例)来自: https://github.com/jedisct1/libsodium.js

<script src="components/sodium/sodium.js" async></script>
<script>
     window.sodium = 
     {  

        onload: function (sodium) 
        {

          function encrypt_and_prepend_nonce(message,password)
          {
            let key = sodium.crypto_generichash(32, sodium.from_string(password));
            let nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
            let nonce_arr = sodium.to_hex(nonce);
            return sodium.from_hex(nonce_arr.concat(sodium.to_hex(sodium.crypto_secretbox_easy(message, nonce, key))));
          }

          function decrypt_after_extracting_nonce(nonce_and_ciphertext,password)
          {
            let key = sodium.crypto_generichash(32, sodium.from_string(password));
            if (nonce_and_ciphertext.length < sodium.crypto_secretbox_NONCEBYTES +
            sodium.crypto_secretbox_MACBYTES)
            {
                throw "Short message";
            }
            let nonce = nonce_and_ciphertext.slice(0, sodium.crypto_secretbox_NONCEBYTES),
            ciphertext = nonce_and_ciphertext.slice(sodium.crypto_secretbox_NONCEBYTES);
            return sodium.crypto_secretbox_open_easy(ciphertext, nonce, key);
          }

          var password = 'user_password';
          let keypair = sodium.crypto_box_keypair();
          let privateKey = keypair.privateKey;
          console.log(privateKey);
          var privateKey_encrypted = encrypt_and_prepend_nonce(privateKey,password);
          var privateKey_decrypted = decrypt_after_extracting_nonce(privateKey_encrypted,password);
          console.log(privateKey_decrypted);
        }
    };
</script>

1 个答案:

答案 0 :(得分:0)

最后找到了。我没想到的是,我正在发送dataType: 'text',从而将密文转换为字符串。正如我最初提到的,我将它与其他用户和与身份验证相关的数据在一个数组中来回发送,因此即使存在dataType二进制函数,对我也不起作用。

问题是我只是在关注和比较“看起来”相同的视觉数据,来回移动。如果运行代码,您将得到一个Uint8Array密文,类似...

244,107,218,84,102,170,55,208,32,148,192,251,218,140,254,204,69,192,24,120,135,88,254,96,56,203,191,65,250,106,42,16,118,179,151,29,220,221,224,6,105,200,235,106,190,248,150,208,233,161,36,4,63,16,2,188,238,21,247,117,4,89,37,43,26,103,135,33,160,44,129,75

与开头的24个字节随机数

串联
244,107,218,84,102,170,55,208,32,148,192,251,218,140,254,204,69,192,24,120,135,88,254,96

解密时,我们将随机数和密文切片,再次使用crypto_secretbox_NONCEBYTES ..也称为“ 24” ...

let nonce = nonce_and_ciphertext.slice(0, sodium.crypto_secretbox_NONCEBYTES),
            ciphertext = nonce_and_ciphertext.slice(sodium.crypto_secretbox_NONCEBYTES);

但是,但是,如果它是字符串,则切片将为:

244,107,218,84,102,170,5

..从而为密文创建了错误的密钥,并导致我需要Stack Overflow和Github帐户。请注意最后一部分,我说过“在返回和输入密码时检查了空格”。...嗯,是的。

无论如何,所以我的解决方案包含非常宽松的示例...首先是加密

<script src="components/sodium/sodium.js" async></script>
<script>
    window.sodium =
    {
        onload: function (sodium)
        {

          function encrypt_and_prepend_nonce(message,password)
          {
            let key = sodium.crypto_generichash(32, sodium.from_string(password));
            let nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
            console.log(nonce);
            let nonce_arr = sodium.to_hex(nonce);
            return sodium.from_hex(nonce_arr.concat(sodium.to_hex(sodium.crypto_secretbox_easy(message, nonce, key))));
          }

          //Password from some text input, but for the example we'll just use a string.
          var password = 'user_password';
          let keypair = sodium.crypto_box_keypair();
          let privateKey = keypair.privateKey;
          var privateKey_encrypted = encrypt_and_prepend_nonce(privateKey,password);

          /* Send it to the server for storage. Just sending public key and password as makes sense
          *  for this example, as we just created the keypair
          */
          $.ajax(
          {
            type: "POST",
            url: "server.php",
            dataType: "text",
            data: 'my_new_encrypted_privateKey=' + privateKey_encrypted + '&publicKey=' + 
            keypair.publicKey + '&user_password=' + password
          });   

        }
    };
</script>

然后解密privateKey ...

<script src="components/sodium/sodium.js" async></script>
<script>
    window.sodium =
    {
        onload: function (sodium)
        {
            //Function to turn privateKey ciphertext string to Uint8Array
            function strToBuffer (string)
            {
                let array = string.split(',');
                let newUint = new Uint8Array(array);
                return newUint;
            }   

            function decrypt_after_extracting_nonce(nonce_and_ciphertext,password)
            {
                let key = sodium.crypto_generichash(32, sodium.from_string(password));
                if (nonce_and_ciphertext.length < sodium.crypto_secretbox_NONCEBYTES +
                sodium.crypto_secretbox_MACBYTES)
                {
                    throw "Short message";
                }
                nonce_and_ciphertext = strToBuffer(nonce_and_ciphertext);
                //now a Uint8Array, will produce the correct nonce & ciphertext slices
                let nonce = nonce_and_ciphertext.slice(0, sodium.crypto_secretbox_NONCEBYTES),
                ciphertext = nonce_and_ciphertext.slice(sodium.crypto_secretbox_NONCEBYTES);
                return sodium.crypto_secretbox_open_easy(ciphertext, nonce, key);
            }

            /* Password from some text input, but for the example we'll just use a string. The privateKey_
             * encrypted will likely be in an ajax callback
             */
            $.ajax(
            {
                type: "POST",
                url: "server.php",
                dataType: "text",
                data: 'some_user_data=' + some_user_data,
                success: function(response) 
                {
                    //Response will be the string...
                    var privateKey_encrypted = response.trim();
                    var privateKey_decrypted = decrypt_after_extracting_nonce(privateKey_encrypted,password);
                    //You can now use your Keypair to open sealed boxes.
                }
            });
        }
    };
</script>