2

我在 java 中使用 AES/GCM/NoPadding 算法(AES-256)加密了一条消息,并试图在 NodeJs 中解密它。解密时出现异常“错误:不支持的状态或无法验证数据”。以下是 java 和 nodejs 的完整代码和错误消息:请帮助我 java 或 nodejs 中的错误代码在哪里。

以下是以 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
4

2 回答 2

2

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

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

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

  • base64 并发送和接收 de-base64 之后,就像你已经做的那样

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

此外,您text已经是 de-base64-ed,但由于它是BufferinputEncodingdecipher.update被忽略。

于 2019-03-24T21:11:44.817 回答
1

在,和模式下使用时必须提供AuthenticationTagto 。createDeCipherIv()AESGCMCCMOCB

没有它你为什么要实施GCMCTR如果您不想要额外的保护,您也可以使用AES 模式。

于 2019-03-24T16:09:33.917 回答