在nodejs解密中出现错误(错误:状态不受支持或无法验证数据)

时间:2018-08-23 06:40:38

标签: java node.js node-crypto

我已经在Java中使用AES / GCM / NoPadding算法(AES-256)对消息进行了加密,并尝试在NodeJ中对其进行解密。解密时出现异常“ 错误:状态不受支持或无法验证数据”。以下是Java和Node.js的完整代码以及错误消息: 请帮助我Java或Node.js中不正确的代码在哪里。

以下是以Java加密代码开头的代码:

 public static String encryptAES(String privateString, String skey) throws Exception{   
    byte[] iv = new byte[GCM_IV_BYTES_LENGTH]; //12 iv length
    byte[] tag = new byte[GCM_TAG_BYTES_LENGTH]; //16 tag length
    (new SecureRandom()).nextBytes(iv);
    (new SecureRandom()).nextBytes(tag);

    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); //algorithm type
    GCMParameterSpec ivSpec = new GCMParameterSpec(GCM_TAG_BYTES_LENGTH * Byte.SIZE, iv);
    cipher.init(Cipher.ENCRYPT_MODE, getKey(skey), ivSpec);

    byte[] ciphertext = cipher.doFinal(privateString.getBytes("UTF8"));

    byte[] ivTag = new byte[GCM_IV_BYTES_LENGTH + GCM_TAG_BYTES_LENGTH]; // merging iv and tag
    System.arraycopy(iv, 0, ivTag, 0, iv.length);
    System.arraycopy(tag, 0, ivTag, iv.length, tag.length);

    byte[] encrypted = new byte[ivTag.length + ciphertext.length]; //merging ivtag and cipher
    System.arraycopy(ivTag, 0, encrypted, 0, ivTag.length);
    System.arraycopy(ciphertext, 0, encrypted, ivTag.length, ciphertext.length);

    String encoded = Base64.getEncoder().encodeToString(encrypted); //b64 encoded value
    System.out.println("encrypted str:>" + encoded.length() + " | " + encoded);
    return encoded;
}

// NodeJS解密代码:

function decryptTokenResponse(encryptedStr){
    let data = encryptedStr
    const bData = Buffer.from(data, 'base64');

    const iv = bData.slice(0, 12);
    const tag = bData.slice(12, 28);
    const text = bData.slice(28);

    var decipher = crypto.createDecipheriv(algorithm,masterkey, iv)
    decipher.setAuthTag(tag)
    var plainText = decipher.update(text,'base64','utf-8');
    plainText += decipher.final('utf-8'); **//getting exception here**
    console.log('Decrypted data = ' + plainText)
}           


**//Error :**

                internal/crypto/cipher.js:145
                  const ret = this._handle.final();
                                           ^

                Error: Unsupported state or unable to authenticate data
                    at Decipheriv.final (internal/crypto/cipher.js:145:28)
                    at decryptTokenResponse (/home/jdoodle.js:40:27)
                    at Object.<anonymous> (/home/jdoodle.js:18:1)
                    at Module._compile (internal/modules/cjs/loader.js:678:30)
                    at Object.Module._extensions..js (internal/modules/cjs/loader.js:689:10)
                    at Module.load (internal/modules/cjs/loader.js:589:32)
                    at tryModuleLoad (internal/modules/cjs/loader.js:528:12)
                    at Function.Module._load (internal/modules/cjs/loader.js:520:3)
                    at Function.Module.runMain (internal/modules/cjs/loader.js:719:10)
                    at startup (internal/bootstrap/node.js:228:19)
                Command exited with non-zero status 1

2 个答案:

答案 0 :(得分:0)

AuthenticationTagcreateDeCipherIv()AES模式下使用GCM时,必须向CCM提供OCB

为什么不使用GCM呢?如果您不想使用其他保护措施,也可以使用CTR AES模式。

答案 1 :(得分:0)

GCM或CCM的authtag是由加密操作生成的 –您不会自己随机生成它(就像您 do 所做的那样,至少可以这样)。 IV / nonce)。但是,它有点隐藏,因为Java加密通过将标签附加到由加密操作返回的密文中或输入到解密操作中,来将经过身份验证的加密适合其预先存在的API。 OTOH nodejs / OpenSSL将它们视为单独的值。 (Java和nodejs / OpenSSL都将AAD视为单独的,但是您没有使用AAD。)

由于您已经将东西打包在一起(和进行base64ing传输),因此您应该:

  • 在Java中,将IV加上cipher.doFinal的返回值(即ctx +标记)连接起来,形成IV + ctx +标记

  • base64,然后像以前一样发送和接收de-base64

  • 在nodejs中,将它们分成IV,ctx,tag,这很容易,因为Buffer可以从两端进行切片:bData.slice(0,12) bData.slice(12,-16) bData.slice(-16)

您的text已经被debase64版本,但是由于它是Buffer,因此对decipher.update的inputEncoding会被忽略。